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
.net
.MalformedURLException
;
14 import java
.util
.ArrayList
;
15 import java
.util
.Arrays
;
16 import java
.util
.HashMap
;
17 import java
.util
.HashSet
;
18 import java
.util
.List
;
21 import java
.util
.UUID
;
22 import java
.util
.regex
.Matcher
;
23 import java
.util
.regex
.Pattern
;
25 import javax
.xml
.stream
.Location
;
26 import javax
.xml
.stream
.XMLEventReader
;
27 import javax
.xml
.stream
.XMLStreamException
;
28 import javax
.xml
.stream
.events
.Attribute
;
29 import javax
.xml
.stream
.events
.StartElement
;
30 import javax
.xml
.stream
.events
.XMLEvent
;
32 import org
.apache
.commons
.lang
.StringUtils
;
33 import org
.apache
.log4j
.Logger
;
35 import eu
.etaxonomy
.cdm
.api
.facade
.DerivedUnitFacade
;
36 import eu
.etaxonomy
.cdm
.api
.facade
.DerivedUnitFacade
.DerivedUnitType
;
37 import eu
.etaxonomy
.cdm
.api
.facade
.DerivedUnitFacadeCacheStrategy
;
38 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
39 import eu
.etaxonomy
.cdm
.ext
.geo
.GeoServiceArea
;
40 import eu
.etaxonomy
.cdm
.ext
.geo
.IEditGeoService
;
41 import eu
.etaxonomy
.cdm
.io
.common
.CdmImportBase
;
42 import eu
.etaxonomy
.cdm
.io
.common
.mapping
.UndefinedTransformerMethodException
;
43 import eu
.etaxonomy
.cdm
.io
.markup
.UnmatchedLeads
.UnmatchedLeadsKey
;
44 import eu
.etaxonomy
.cdm
.model
.agent
.AgentBase
;
45 import eu
.etaxonomy
.cdm
.model
.agent
.INomenclaturalAuthor
;
46 import eu
.etaxonomy
.cdm
.model
.agent
.Team
;
47 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
48 import eu
.etaxonomy
.cdm
.model
.common
.AnnotatableEntity
;
49 import eu
.etaxonomy
.cdm
.model
.common
.Annotation
;
50 import eu
.etaxonomy
.cdm
.model
.common
.AnnotationType
;
51 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
52 import eu
.etaxonomy
.cdm
.model
.common
.Extension
;
53 import eu
.etaxonomy
.cdm
.model
.common
.ExtensionType
;
54 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
55 import eu
.etaxonomy
.cdm
.model
.common
.TermVocabulary
;
56 import eu
.etaxonomy
.cdm
.model
.common
.TimePeriod
;
57 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
58 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
59 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
60 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
61 import eu
.etaxonomy
.cdm
.model
.description
.IndividualsAssociation
;
62 import eu
.etaxonomy
.cdm
.model
.description
.KeyStatement
;
63 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKey
;
64 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKeyNode
;
65 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTermBase
;
66 import eu
.etaxonomy
.cdm
.model
.description
.PresenceTerm
;
67 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
68 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
69 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
70 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaLevel
;
71 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaType
;
72 import eu
.etaxonomy
.cdm
.model
.media
.IdentifiableMediaEntity
;
73 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
74 import eu
.etaxonomy
.cdm
.model
.name
.CultivarPlantName
;
75 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
76 import eu
.etaxonomy
.cdm
.model
.name
.NameTypeDesignationStatus
;
77 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalCode
;
78 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatus
;
79 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatusType
;
80 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
81 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
82 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignationStatus
;
83 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
84 import eu
.etaxonomy
.cdm
.model
.occurrence
.Collection
;
85 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnitBase
;
86 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldObservation
;
87 import eu
.etaxonomy
.cdm
.model
.occurrence
.Specimen
;
88 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
89 import eu
.etaxonomy
.cdm
.model
.reference
.IArticle
;
90 import eu
.etaxonomy
.cdm
.model
.reference
.IJournal
;
91 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
92 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceFactory
;
93 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceType
;
94 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
95 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationshipType
;
96 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
97 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
98 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.UnknownCdmTypeException
;
99 import eu
.etaxonomy
.cdm
.strategy
.parser
.NameTypeParser
;
100 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImpl
;
101 import eu
.etaxonomy
.cdm
.strategy
.parser
.SpecimenTypeParser
;
102 import eu
.etaxonomy
.cdm
.strategy
.parser
.SpecimenTypeParser
.TypeInfo
;
108 public class MarkupDocumentImportNoComponent
extends MarkupImportBase
{
109 private static final Logger logger
= Logger
.getLogger(MarkupDocumentImportNoComponent
.class);
111 private static final boolean CREATE_NEW
= true;
112 private static final boolean IS_IMAGE_GALLERY
= true;
113 private static final boolean NO_IMAGE_GALLERY
= false;
116 private static final String ACCEPTED
= "accepted";
117 private static final String ACCEPTED_NAME
= "acceptedName";
118 private static final String ADDENDA
= "addenda";
119 private static final String ALTERNATEPUBTITLE
= "alternatepubtitle";
120 private static final String ALTERNATIVE_COLLECTION_TYPE_STATUS
= "alternativeCollectionTypeStatus";
121 private static final String ALTERNATIVE_COLLECTOR
= "alternativeCollector";
122 private static final String ALTERNATIVE_FIELD_NUM
= "alternativeFieldNum";
123 private static final String ALTITUDE
= "altitude";
124 private static final String ANNOTATION
= "annotation";
125 private static final String AUTHOR
= "author";
126 private static final String BIBLIOGRAPHY
= "bibliography";
127 private static final String BIOGRAPHIES
= "biographies";
128 private static final String BOLD
= "bold";
129 private static final String BR
= "br";
130 private static final String CHAR
= "char";
131 private static final String CITATION
= "citation";
132 private static final String COLLECTION_AND_TYPE
= "collectionAndType";
133 private static final String COLLECTION_TYPE_STATUS
= "collectionTypeStatus";
134 private static final String COLLECTOR
= "collector";
135 private static final String COLLECTION
= "collection";
136 private static final String COORDINATES
= "coordinates";
137 private static final String COUPLET
= "couplet";
138 private static final String DATES
= "dates";
139 private static final String DEDICATION
= "dedication";
140 private static final String DEFAULT_MEDIA_URL
= "defaultMediaUrl";
141 private static final String DESTROYED
= "destroyed";
142 private static final String DETAILS
= "details";
143 private static final String DISTRIBUTION_LIST
= "distributionList";
144 private static final String DISTRIBUTION_LOCALITY
= "distributionLocality";
145 private static final String EDITION
= "edition";
146 private static final String EDITORS
= "editors";
147 private static final String FEATURE
= "feature";
148 private static final String FIGURE
= "figure";
149 private static final String FIGURE_LEGEND
= "figureLegend";
150 private static final String FIGURE_PART
= "figurePart";
151 private static final String FIGURE_REF
= "figureRef";
152 private static final String FIGURE_TITLE
= "figureTitle";
153 private static final String FOOTNOTE
= "footnote";
154 private static final String FOOTNOTE_REF
= "footnoteRef";
155 private static final String FOOTNOTE_STRING
= "footnoteString";
156 private static final String FIELD_NUM
= "fieldNum";
157 private static final String FREQUENCY
= "frequency";
158 private static final String FULL_NAME
= "fullName";
159 private static final String FULL_TYPE
= "fullType";
160 private static final String GATHERING
= "gathering";
161 private static final String HEADING
= "heading";
162 private static final String HABITAT
= "habitat";
163 private static final String HABITAT_LIST
= "habitatList";
164 private static final String HOMONYM
= "homonym";
165 private static final String HOMOTYPES
= "homotypes";
166 private static final String ID
= "id";
167 private static final String INFRANK
= "infrank";
168 private static final String INFRAUT
= "infraut";
169 private static final String INFRPARAUT
= "infrparaut";
170 private static final String IS_SPOTCHARACTERS
= "isSpotcharacters";
171 private static final String ISSUE
= "issue";
172 private static final String ITALICS
= "italics";
173 private static final String KEY
= "key";
174 private static final String KEY_TITLE
= "keyTitle";
175 private static final String KEYNOTES
= "keynotes";
176 private static final String LIFE_CYCLE_PERIODS
= "lifeCyclePeriods";
177 private static final String LOCALITY
= "locality";
178 private static final String LOST
= "lost";
179 private static final String META_DATA
= "metaData";
180 private static final String NAME
= "name";
181 private static final String NAME_TYPE
= "nameType";
182 private static final String NOM
= "nom";
183 private static final String NOMENCLATURE
= "nomenclature";
184 private static final String NOT_FOUND
= "notFound";
185 private static final String NOT_SEEN
= "notSeen";
186 private static final String NOTES
= "notes";
187 private static final String NUM
= "num";
188 private static final String ORIGINAL_DETERMINATION
= "originalDetermination";
189 private static final String PAGES
= "pages";
190 private static final String PARAUT
= "paraut";
191 private static final String PUBFULLNAME
= "pubfullname";
192 private static final String PUBLICATION
= "publication";
193 private static final String PUBNAME
= "pubname";
194 private static final String PUBTITLE
= "pubtitle";
195 private static final String PUBTYPE
= "pubtype";
196 private static final String QUESTION
= "question";
197 private static final String QUOTE
= "quote";
198 private static final String RANK
= "rank";
199 private static final String REF
= "ref";
200 private static final String REF_NUM
= "refNum";
201 private static final String REF_PART
= "refPart";
202 private static final String REFERENCE
= "reference";
203 private static final String REFERENCES
= "references";
204 private static final String TAXON
= "taxon";
205 private static final String TAXONTITLE
= "taxontitle";
206 private static final String TAXONTYPE
= "taxontype";
207 private static final String TEXT
= "text";
208 private static final String TEXT_SECTION
= "textSection";
209 private static final String TO_COUPLET
= "toCouplet";
210 private static final String TO_KEY
= "toKey";
211 private static final String TO_TAXON
= "toTaxon";
212 private static final String TYPE
= "type";
213 private static final String TYPE_STATUS
= "typeStatus";
214 private static final String TREATMENT
= "treatment";
215 private static final String SERIALS_ABBREVIATIONS
= "serialsAbbreviations";
216 private static final String SPECIMEN_TYPE
= "specimenType";
217 private static final String STATUS
= "status";
218 private static final String STRING
= "string";
219 private static final String SUB_HEADING
= "subHeading";
220 private static final String SUB_COLLECTION
= "subCollection";
221 private static final String SYNONYM
= "synonym";
222 private static final String UNKNOWN
= "unknown";
223 private static final String URL
= "url";
224 private static final String USAGE
= "usage";
225 private static final String VOLUME
= "volume";
226 private static final String WRITER
= "writer";
227 private static final String YEAR
= "year";
229 private NonViralNameParserImpl parser
= new NonViralNameParserImpl();
231 // TODO make part of state, but state is renewed when invoking the import a
233 private UnmatchedLeads unmatchedLeads
;
236 private IEditGeoService editGeoService
;
238 public MarkupDocumentImportNoComponent(MarkupDocumentImport docImport
) {
240 this.editGeoService
= docImport
.getEditGeoService();
243 public void doInvoke(MarkupImportState state
) throws XMLStreamException
{
244 XMLEventReader reader
= state
.getReader();
247 String elName
= PUBLICATION
;
248 boolean hasPublication
= false;
250 while (reader
.hasNext()) {
251 XMLEvent nextEvent
= reader
.nextEvent();
252 if (isStartingElement(nextEvent
, elName
)) {
253 handlePublication(state
, reader
, nextEvent
, elName
);
254 hasPublication
= true;
255 } else if (nextEvent
.isEndDocument()) {
256 if (!hasPublication
) {
257 String message
= "No publication root element found";
258 fireWarningEvent(message
, nextEvent
, 8);
262 fireSchemaConflictEventExpectedStartTag(elName
, reader
);
271 private void handlePublication(MarkupImportState state
, XMLEventReader reader
, XMLEvent currentEvent
,
272 String elName
) throws XMLStreamException
{
275 StartElement element
= currentEvent
.asStartElement();
276 Map
<String
, Attribute
> attributes
= getAttributes(element
);
277 String lang
= getAndRemoveAttributeValue(attributes
, "lang");
279 Language language
= getTermService().getLanguageByIso(lang
);
280 state
.setDefaultLanguage(language
);
283 handleUnexpectedAttributes(element
.getLocation(), attributes
, "noNamespaceSchemaLocation");
285 while (reader
.hasNext()) {
286 XMLEvent event
= readNoWhitespace(reader
);
287 // TODO cardinality of alternative
288 if (event
.isEndElement()) {
289 if (isEndingElement(event
, elName
)) {
292 if (isEndingElement(event
, BIOGRAPHIES
)) {
293 // NOT YET IMPLEMENTED
294 popUnimplemented(event
.asEndElement());
295 } else if (isEndingElement(event
, REFERENCES
)) {
296 // NOT YET IMPLEMENTED
297 popUnimplemented(event
.asEndElement());
298 } else if (isEndingElement(event
, TEXT_SECTION
)) {
299 // NOT YET IMPLEMENTED
300 popUnimplemented(event
.asEndElement());
301 } else if (isEndingElement(event
, ADDENDA
)) {
302 // NOT YET IMPLEMENTED
303 popUnimplemented(event
.asEndElement());
305 handleUnexpectedElement(event
);
308 } else if (event
.isStartElement()) {
309 if (isStartingElement(event
, META_DATA
)) {
310 handleMetaData(state
, reader
, event
);
311 } else if (isStartingElement(event
, TREATMENT
)) {
312 handleTreatment(state
, reader
, event
);
313 } else if (isStartingElement(event
, BIOGRAPHIES
)) {
314 handleNotYetImplementedElement(event
);
315 } else if (isStartingElement(event
, REFERENCES
)) {
316 handleNotYetImplementedElement(event
);
317 } else if (isStartingElement(event
, TEXT_SECTION
)) {
318 handleNotYetImplementedElement(event
);
319 } else if (isStartingElement(event
, ADDENDA
)) {
320 handleNotYetImplementedElement(event
);
322 handleUnexpectedStartElement(event
);
325 handleUnexpectedElement(event
);
328 throw new IllegalStateException("Publication has no ending element");
331 private void handleMetaData(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
332 checkNoAttributes(parentEvent
);
334 while (reader
.hasNext()) {
335 XMLEvent next
= readNoWhitespace(reader
);
336 if (isMyEndingElement(next
, parentEvent
)) {
338 } else if (isStartingElement(next
, DEFAULT_MEDIA_URL
)) {
339 String baseUrl
= getCData(state
, reader
, next
);
342 state
.setBaseMediaUrl(baseUrl
);
343 } catch (MalformedURLException e
) {
344 String message
= "defaultMediaUrl '%s' is not a valid URL";
345 message
= String
.format(message
, baseUrl
);
346 fireWarningEvent(message
, next
, 8);
349 handleUnexpectedElement(next
);
352 throw new IllegalStateException("MetaData has no ending element");
356 private void handleTreatment(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
357 checkNoAttributes(parentEvent
);
358 Taxon lastTaxon
= null;
359 while (reader
.hasNext()) {
360 XMLEvent next
= readNoWhitespace(reader
);
361 if (isStartingElement(next
, TAXON
)) {
362 Taxon thisTaxon
= handleTaxon(state
, reader
, next
.asStartElement());
363 doTaxonRelation(state
, thisTaxon
, lastTaxon
, parentEvent
.getLocation());
364 lastTaxon
= thisTaxon
;
365 // TODO for imports spanning multiple documents ?? Still needed?
366 state
.getConfig().setLastTaxonUuid(lastTaxon
.getUuid());
367 } else if (isMyEndingElement(next
, parentEvent
)) {
368 Set
<PolytomousKeyNode
> keyNodesToSave
= state
.getPolytomousKeyNodesToSave();
369 //better save the key then the nodes
370 Set
<PolytomousKey
> keySet
= new HashSet
<PolytomousKey
>();
371 for (PolytomousKeyNode node
: keyNodesToSave
){
372 PolytomousKey key
= node
.getKey();
376 //unmatched key leads
377 UnmatchedLeads unmatched
= state
.getUnmatchedLeads();
378 if (unmatched
.size() > 0){
379 String message
= "The following key leads are unmatched: %s";
380 message
= String
.format(message
, state
.getUnmatchedLeads().toString());
381 fireWarningEvent(message
, next
, 6);
383 // save(keyNodesToSave, state);
387 handleUnexpectedElement(next
);
397 private void doTaxonRelation(MarkupImportState state
, Taxon taxon
,
398 Taxon lastTaxon
, Location dataLocation
) {
400 Classification tree
= makeTree(state
, dataLocation
);
401 if (lastTaxon
== null) {
402 tree
.addChildTaxon(taxon
, null, null, null);
405 Rank thisRank
= taxon
.getName().getRank();
406 Rank lastRank
= lastTaxon
.getName().getRank();
407 if (lastTaxon
.getTaxonNodes().size() > 0) {
408 TaxonNode lastNode
= lastTaxon
.getTaxonNodes().iterator().next();
409 if (thisRank
== null){
410 String message
= "rank is undefined for taxon '%s'. Can't create classification without rank.";
411 message
= String
.format(message
, taxon
.getName().getTitleCache());
412 fireWarningEvent(message
, makeLocationStr(dataLocation
), 6);
413 }else if (thisRank
.isLower(lastRank
)) {
414 lastNode
.addChildTaxon(taxon
, null, null, null);
415 fillMissingEpithetsForTaxa(lastTaxon
, taxon
);
416 } else if (thisRank
.equals(lastRank
)) {
417 TaxonNode parent
= lastNode
.getParent();
418 if (parent
!= null) {
419 parent
.addChildTaxon(taxon
, null, null, null);
420 fillMissingEpithetsForTaxa(parent
.getTaxon(), taxon
);
422 tree
.addChildTaxon(taxon
, null, null, null);
424 } else if (thisRank
.isHigher(lastRank
)) {
425 doTaxonRelation(state
, taxon
, lastNode
.getParent().getTaxon(), dataLocation
);
426 // TaxonNode parentNode = handleTaxonRelation(state, taxon,
427 // lastNode.getParent().getTaxon());
428 // parentNode.addChildTaxon(taxon, null, null, null);
432 String message
= "Last taxon has no node";
433 fireWarningEvent(message
, makeLocationStr(dataLocation
), 6);
441 * @param dataLocation
444 private Classification
makeTree(MarkupImportState state
, Location dataLocation
) {
445 Classification result
= state
.getTree(null);
446 if (result
== null) {
447 UUID uuid
= state
.getConfig().getClassificationUuid();
449 String message
= "No classification uuid is defined";
450 fireWarningEvent(message
, makeLocationStr(dataLocation
), 6);
451 result
= createNewClassification(state
);
453 result
= getClassificationService().find(uuid
);
454 if (result
== null) {
455 result
= createNewClassification(state
);
456 result
.setUuid(uuid
);
459 state
.putTree(null, result
);
465 private Classification
createNewClassification(MarkupImportState state
) {
466 Classification result
;
467 result
= Classification
.NewInstance(state
.getConfig().getClassificationTitle());
468 state
.putTree(null, result
);
472 private Taxon
handleTaxon(MarkupImportState state
, XMLEventReader reader
, StartElement parentEvent
) throws XMLStreamException
{
473 // TODO progress monitoring
474 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
475 Taxon taxon
= createTaxonAndName(state
, attributes
);
476 state
.setCurrentTaxon(taxon
);
478 boolean hasTitle
= false;
479 boolean hasNomenclature
= false;
480 String taxonTitle
= null;
482 while (reader
.hasNext()) {
483 XMLEvent next
= readNoWhitespace(reader
);
484 if (next
.isEndElement()) {
485 if (isMyEndingElement(next
, parentEvent
)) {
486 checkMandatoryElement(hasTitle
, parentEvent
, TAXONTITLE
);
487 checkMandatoryElement(hasNomenclature
, parentEvent
, NOMENCLATURE
);
488 handleUnexpectedAttributes(parentEvent
.getLocation(),attributes
);
489 if (taxon
.getName().getRank() == null){
490 String warning
= "No rank exists for taxon " + taxon
.getTitleCache();
491 fireWarningEvent(warning
, next
, 12);
494 makeKeyNodes(state
, parentEvent
, taxonTitle
);
495 state
.setCurrentTaxon(null);
496 state
.setCurrentTaxonNum(null);
500 if (isEndingElement(next
, HEADING
)) {
501 // NOT YET IMPLEMENTED
502 popUnimplemented(next
.asEndElement());
503 } else if (isEndingElement(next
, TEXT_SECTION
)) {
504 // NOT YET IMPLEMENTED
505 popUnimplemented(next
.asEndElement());
506 } else if (isEndingElement(next
, REFERENCES
)) {
507 // NOT YET IMPLEMENTED
508 popUnimplemented(next
.asEndElement());
509 } else if (isEndingElement(next
, FIGURE_REF
)) {
510 // NOT YET IMPLEMENTED
511 popUnimplemented(next
.asEndElement());
513 handleUnexpectedEndElement(next
.asEndElement());
516 } else if (next
.isStartElement()) {
517 if (isStartingElement(next
, HEADING
)) {
518 handleNotYetImplementedElement(next
);
519 } else if (isStartingElement(next
, TAXONTITLE
)) {
520 taxonTitle
= handleTaxonTitle(state
, reader
, next
);
522 } else if (isStartingElement(next
, WRITER
)) {
523 makeKeyWriter(state
, reader
, taxon
, taxonTitle
, next
);
524 } else if (isStartingElement(next
, TEXT_SECTION
)) {
525 handleNotYetImplementedElement(next
);
526 } else if (isStartingElement(next
, KEY
)) {
527 handleKey(state
, reader
, next
);
528 } else if (isStartingElement(next
, NOMENCLATURE
)) {
529 handleNomenclature(state
, reader
, next
);
530 hasNomenclature
= true;
531 } else if (isStartingElement(next
, FEATURE
)) {
532 handleFeature(state
, reader
, next
);
533 } else if (isStartingElement(next
, NOTES
)) {
534 // TODO is this the correct way to handle notes?
535 String note
= handleNotes(state
, reader
, next
);
539 notesUuid
= state
.getTransformer().getFeatureUuid(
541 Feature feature
= getFeature(state
, notesUuid
, "Notes",
542 "Notes", "note", null);
543 TextData textData
= TextData
.NewInstance(feature
);
544 textData
.putText(Language
.DEFAULT(), note
);
545 TaxonDescription description
= getTaxonDescription(
546 taxon
, null, false, true);
547 description
.addElement(textData
);
548 } catch (UndefinedTransformerMethodException e
) {
549 String message
= "getFeatureUuid method not yet implemented";
550 fireWarningEvent(message
, next
, 8);
552 } else if (isStartingElement(next
, REFERENCES
)) {
553 handleNotYetImplementedElement(next
);
554 } else if (isStartingElement(next
, FIGURE_REF
)) {
555 handleNotYetImplementedElement(next
);
556 } else if (isStartingElement(next
, FIGURE
)) {
557 handleFigure(state
, reader
, next
);
558 } else if (isStartingElement(next
, FOOTNOTE
)) {
559 FootnoteDataHolder footnote
= handleFootnote(state
, reader
, next
);
560 if (footnote
.isRef()) {
561 String message
= "Ref footnote not implemented here";
562 fireWarningEvent(message
, next
, 4);
564 registerGivenFootnote(state
, footnote
);
567 handleUnexpectedStartElement(next
);
570 handleUnexpectedElement(next
);
573 throw new IllegalStateException("<Taxon> has no closing tag");
576 private void makeKeyNodes(MarkupImportState state
, XMLEvent event
, String taxonTitle
) {
577 Taxon taxon
= state
.getCurrentTaxon();
578 String num
= state
.getCurrentTaxonNum();
580 String nameString
= CdmBase
.deproxy(taxon
.getName(), NonViralName
.class).getNameCache();
581 // String nameString = taxonTitle;
583 //try to find matching lead nodes
584 UnmatchedLeadsKey leadsKey
= UnmatchedLeadsKey
.NewInstance(num
, nameString
);
585 Set
<PolytomousKeyNode
> matchingNodes
= handleMatchingNodes(state
, taxon
, leadsKey
);
587 if (num
!= null){//same without using the num
588 UnmatchedLeadsKey noNumLeadsKey
= UnmatchedLeadsKey
.NewInstance("", nameString
);
589 Set
<PolytomousKeyNode
> noNumMatchingNodes
= handleMatchingNodes(state
, taxon
, noNumLeadsKey
);
590 if(noNumMatchingNodes
.size() > 0){
591 String message
="Taxon matches additional key node when not considering <num> attribute in taxontitle. This may be correct but may also indicate an error.";
592 fireWarningEvent(message
, event
, 1);
595 //report missing match, if num exists
596 if (matchingNodes
.isEmpty() && num
!= null){
597 String message
= "Taxon has <num> attribute in taxontitle but no matching key nodes exist: %s, Key: %s";
598 message
= String
.format(message
, num
, leadsKey
.toString());
599 fireWarningEvent(message
, event
, 1);
604 private Set
<PolytomousKeyNode
> handleMatchingNodes(MarkupImportState state
, Taxon taxon
, UnmatchedLeadsKey leadsKey
) {
605 Set
<PolytomousKeyNode
> matchingNodes
= state
.getUnmatchedLeads().getNodes(leadsKey
);
606 for (PolytomousKeyNode matchingNode
: matchingNodes
){
607 state
.getUnmatchedLeads().removeNode(leadsKey
, matchingNode
);
608 matchingNode
.setTaxon(taxon
);
609 state
.getPolytomousKeyNodesToSave().add(matchingNode
);
611 return matchingNodes
;
614 private void handleKey(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
616 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
617 String isSpotcharacters
= getAndRemoveAttributeValue(attributes
, IS_SPOTCHARACTERS
);
618 if (isNotBlank(isSpotcharacters
) ) {
619 //TODO isSpotcharacters
620 String message
= "Attribute isSpotcharacters not yet implemented for <key>";
621 fireWarningEvent(message
, parentEvent
, 4);
624 PolytomousKey key
= PolytomousKey
.NewInstance();
625 key
.addTaxonomicScope(state
.getCurrentTaxon());
626 state
.setCurrentKey(key
);
628 boolean isFirstCouplet
= true;
629 while (reader
.hasNext()) {
630 XMLEvent next
= readNoWhitespace(reader
);
631 if (isMyEndingElement(next
, parentEvent
)) {
633 state
.setCurrentKey(null);
635 } else if (isEndingElement(next
, KEYNOTES
)){
636 popUnimplemented(next
.asEndElement());
637 } else if (isStartingElement(next
, KEY_TITLE
)) {
638 handleKeyTitle(state
, reader
, next
);
639 } else if (isStartingElement(next
, KEYNOTES
)) {
641 handleNotYetImplementedElement(next
);
642 } else if (isStartingElement(next
, COUPLET
)) {
643 PolytomousKeyNode node
= null;
645 node
= key
.getRoot();
646 isFirstCouplet
= false;
648 handleCouplet(state
, reader
, next
, node
);
650 handleUnexpectedElement(next
);
653 throw new IllegalStateException("<key> has no closing tag");
661 * @throws XMLStreamException
663 private void handleKeyTitle(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
664 PolytomousKey key
= state
.getCurrentKey();
665 String keyTitle
= getCData(state
, reader
, parentEvent
);
666 String standardTitles
= "(?i)(Key\\sto\\sthe\\s(genera|species|varieties|forms))";
668 if (isNotBlank(keyTitle
) ){
669 if (!state
.getConfig().isReplaceStandardKeyTitles() || ! keyTitle
.matches(standardTitles
)){
670 key
.setTitleCache(keyTitle
, true);
675 private void handleCouplet(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
, PolytomousKeyNode parentNode
) throws XMLStreamException
{
676 String num
= getOnlyAttribute(parentEvent
, NUM
, true);
677 List
<PolytomousKeyNode
> childList
= new ArrayList
<PolytomousKeyNode
>();
679 while (reader
.hasNext()) {
680 XMLEvent next
= readNoWhitespace(reader
);
681 if (isMyEndingElement(next
, parentEvent
)) {
682 completeCouplet(state
, parentEvent
, parentNode
, num
, childList
);
684 } else if (isStartingElement(next
, QUESTION
)) {
685 handleQuestion(state
, reader
, next
, childList
);
686 } else if (isStartingElement(next
, KEYNOTES
)) {
688 handleNotYetImplementedElement(next
);
689 } else if (isEndingElement(next
, KEYNOTES
)) {
691 popUnimplemented(next
.asEndElement());
693 handleUnexpectedElement(next
);
696 throw new IllegalStateException("<couplet> has no closing tag");
706 private void completeCouplet(MarkupImportState state
, XMLEvent parentEvent
,
707 PolytomousKeyNode parentNode
, String num
, List
<PolytomousKeyNode
> childList
) {
708 if (parentNode
!= null){
709 for (PolytomousKeyNode childNode
: childList
){
710 parentNode
.addChild(childNode
);
712 }else if (isNotBlank(num
)){
713 UnmatchedLeadsKey unmatchedKey
= UnmatchedLeadsKey
.NewInstance(state
.getCurrentKey(), num
);
714 Set
<PolytomousKeyNode
> nodes
= state
.getUnmatchedLeads().getNodes(unmatchedKey
);
715 for(PolytomousKeyNode nodeToMatch
: nodes
){
716 for (PolytomousKeyNode childNode
: childList
){
717 nodeToMatch
.addChild(childNode
);
719 state
.getUnmatchedLeads().removeNode(unmatchedKey
, nodeToMatch
);
722 String message
= "Parent num could not be matched. Please check if num (%s) is correct";
723 message
= String
.format(message
, num
);
724 fireWarningEvent(message
, parentEvent
, 6);
728 private void handleQuestion(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
, List
<PolytomousKeyNode
> nodesList
) throws XMLStreamException
{
730 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
731 //needed only for data lineage
732 String questionNum
= getAndRemoveRequiredAttributeValue(parentEvent
, attributes
, NUM
);
734 PolytomousKeyNode myNode
= PolytomousKeyNode
.NewInstance();
735 myNode
.setKey(state
.getCurrentKey()); //to avoid NPE while computing num in PolytomousKeyNode in case this node is not matched correctly with a parent
736 nodesList
.add(myNode
);
738 while (reader
.hasNext()) {
739 XMLEvent next
= readNoWhitespace(reader
);
740 if (isMyEndingElement(next
, parentEvent
)) {
742 } else if (isStartingElement(next
, TEXT
)) {
743 String text
= getCData(state
, reader
, next
);
744 KeyStatement statement
= KeyStatement
.NewInstance(text
);
745 myNode
.setStatement(statement
);
746 } else if (isStartingElement(next
, COUPLET
)) {
748 handleCouplet(state
, reader
, next
, myNode
);
749 } else if (isStartingElement(next
, TO_COUPLET
)) {
750 handleToCouplet(state
, reader
, next
, myNode
);
751 } else if (isStartingElement(next
, TO_TAXON
)) {
752 handleToTaxon(state
, reader
, next
, myNode
);
753 } else if (isStartingElement(next
, TO_KEY
)) {
755 handleNotYetImplementedElement(next
);
756 } else if (isEndingElement(next
, TO_KEY
)){
758 popUnimplemented(next
.asEndElement());
759 } else if (isStartingElement(next
, KEYNOTES
)) {
761 handleNotYetImplementedElement(next
);
762 } else if (isEndingElement(next
, KEYNOTES
)){
764 popUnimplemented(next
.asEndElement());
766 handleUnexpectedElement(next
);
769 throw new IllegalStateException("<question> has no closing tag");
772 private void handleToCouplet(MarkupImportState state
, XMLEventReader reader
, XMLEvent next
, PolytomousKeyNode node
) throws XMLStreamException
{
773 String num
= getOnlyAttribute(next
, NUM
, true);
774 String cData
= getCData(state
, reader
, next
, false);
775 if (isNotBlank(cData
) && ! cData
.equals(num
)){
776 String message
= "CData ('%s') not handled in <toCouplet>";
777 message
= String
.format(message
, cData
);
778 fireWarningEvent(message
, next
, 4);
780 UnmatchedLeadsKey unmatched
= UnmatchedLeadsKey
.NewInstance(state
.getCurrentKey(), num
);
781 state
.getUnmatchedLeads().addKey(unmatched
, node
);
784 private void handleToTaxon(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
, PolytomousKeyNode node
) throws XMLStreamException
{
785 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
786 String num
= getAndRemoveAttributeValue(attributes
, NUM
);
787 String taxonStr
= getCData(state
, reader
, parentEvent
, false);
789 taxonStr
= makeTaxonKey(taxonStr
, state
.getCurrentTaxon());
790 UnmatchedLeadsKey unmatched
= UnmatchedLeadsKey
.NewInstance(num
, taxonStr
);
791 state
.getUnmatchedLeads().addKey(unmatched
, node
);
795 private String
makeTaxonKey(String strGoto
, Taxon taxon
) {
797 if (strGoto
== null){
801 NonViralName
<?
> name
= CdmBase
.deproxy(taxon
.getName(), NonViralName
.class);
802 String strGenusName
= name
.getGenusOrUninomial();
805 strGoto
= strGoto
.replaceAll("\\([^\\(\\)]*\\)", ""); //replace all brackets
806 strGoto
= strGoto
.replaceAll("\\s+", " "); //replace multiple whitespaces by exactly one whitespace
808 strGoto
= strGoto
.trim();
809 String
[] split
= strGoto
.split("\\s");
810 for (int i
= 0; i
<split
.length
; i
++){
811 String single
= split
[i
];
812 if (isGenusAbbrev(single
, strGenusName
)){
813 split
[i
] = strGenusName
;
815 if (isInfraSpecificMarker(single
)){
816 String strSpeciesEpi
= name
.getSpecificEpithet();
817 if (isBlank(result
)){
818 result
+= strGenusName
+ " " + strSpeciesEpi
;
821 result
= (result
+ " " + split
[i
]).trim();
827 private boolean isInfraSpecificMarker(String single
) {
829 if (Rank
.getRankByAbbreviation(single
).isInfraSpecific()){
834 } catch (UnknownCdmTypeException e
) {
839 private boolean isGenusAbbrev(String single
, String strGenusName
) {
840 if (! single
.matches("[A-Z]\\.?")) {
842 }else if (single
.length() == 0 || strGenusName
== null || strGenusName
.length() == 0){
845 return single
.charAt(0) == strGenusName
.charAt(0);
855 * @throws XMLStreamException
857 private void makeKeyWriter(MarkupImportState state
, XMLEventReader reader
, Taxon taxon
, String taxonTitle
, XMLEvent next
) throws XMLStreamException
{
858 WriterDataHolder writer
= handleWriter(state
, reader
, next
);
859 taxon
.addExtension(writer
.extension
);
860 // TODO what if taxonTitle comes later
861 if (StringUtils
.isNotBlank(taxonTitle
)
862 && writer
.extension
!= null) {
863 Reference
<?
> sec
= ReferenceFactory
.newBookSection();
864 sec
.setTitle(taxonTitle
);
865 TeamOrPersonBase
<?
> author
= createAuthor(writer
.writer
);
866 sec
.setAuthorTeam(author
);
867 sec
.setInReference(state
.getConfig()
868 .getSourceReference());
870 registerFootnotes(state
, sec
, writer
.footnotes
);
872 String message
= "No taxontitle exists for writer";
873 fireWarningEvent(message
, next
, 6);
877 private String
handleNotes(MarkupImportState state
, XMLEventReader reader
,
878 XMLEvent parentEvent
) throws XMLStreamException
{
879 checkNoAttributes(parentEvent
);
882 while (reader
.hasNext()) {
883 XMLEvent next
= readNoWhitespace(reader
);
884 if (isMyEndingElement(next
, parentEvent
)) {
886 } else if (next
.isEndElement()) {
887 if (isEndingElement(next
, HEADING
)) {
888 popUnimplemented(next
.asEndElement());
889 } else if (isEndingElement(next
, WRITER
)) {
890 popUnimplemented(next
.asEndElement());
891 } else if (isEndingElement(next
, NUM
)) {
892 popUnimplemented(next
.asEndElement());
894 handleUnexpectedEndElement(next
.asEndElement());
896 } else if (next
.isStartElement()) {
897 if (isStartingElement(next
, HEADING
)) {
898 handleNotYetImplementedElement(next
);
899 } else if (isStartingElement(next
, SUB_HEADING
)) {
900 String subheading
= getCData(state
, reader
, next
).trim();
901 if (! isNoteHeading(subheading
)) {
902 fireNotYetImplementedElement(next
.getLocation(), next
.asStartElement().getName(), 0);
904 } else if (isStartingElement(next
, WRITER
)) {
905 handleNotYetImplementedElement(next
);
906 } else if (isStartingElement(next
, NUM
)) {
907 handleNotYetImplementedElement(next
);
908 } else if (isStartingElement(next
, STRING
)) {
909 // TODO why multiple strings in schema?
910 text
= makeNotesString(state
, reader
, text
, next
);
912 handleUnexpectedStartElement(next
.asStartElement());
915 handleUnexpectedElement(next
);
918 throw new IllegalStateException("<Notes> has no closing tag");
927 * @throws XMLStreamException
929 private String
makeNotesString(MarkupImportState state
, XMLEventReader reader
, String text
, XMLEvent next
) throws XMLStreamException
{
930 Map
<String
, String
> stringMap
= handleString(state
, reader
, next
, null);
931 if (stringMap
.size() == 0){
932 String message
= "No text available in <notes>";
933 fireWarningEvent(message
, next
, 4);
934 }else if (stringMap
.size() > 1){
935 String message
= "Subheadings not yet supported in <notes>";
936 fireWarningEvent(message
, next
, 4);
938 String firstSubheading
= stringMap
.keySet().iterator().next();
939 if ( firstSubheading
!= null && ! isNoteHeading (firstSubheading
) ) {
940 String message
= "Subheadings not yet supported in <notes>";
941 fireWarningEvent(message
, next
, 4);
944 for (String subheading
: stringMap
.keySet()){
946 text
+= stringMap
.get(subheading
);
951 private boolean isNoteHeading(String heading
) {
952 String excludePattern
= "(i?)(Notes?):?";
953 return heading
.matches(excludePattern
);
960 private Taxon
createTaxonAndName(MarkupImportState state
,
961 Map
<String
, Attribute
> attributes
) {
962 NonViralName
<?
> name
;
963 Rank rank
= null; //Rank.SPECIES(); // default
964 boolean isCultivar
= checkAndRemoveAttributeValue(attributes
, CLASS
, "cultivated");
966 name
= CultivarPlantName
.NewInstance(rank
);
968 name
= createNameByCode(state
, rank
);
970 Taxon taxon
= Taxon
.NewInstance(name
, state
.getConfig().getSourceReference());
971 if (checkAndRemoveAttributeValue(attributes
, CLASS
, "dubious")) {
972 taxon
.setDoubtful(true);
973 } else if (checkAndRemoveAttributeValue(attributes
, CLASS
, "excluded")) {
974 taxon
.setExcluded(true);
976 // TODO insufficient, new, expected
977 handleNotYetImplementedAttribute(attributes
, CLASS
);
979 // MarkerType markerType = getMarkerType(state, attrValue);
980 // if (markerType == null){
981 // logger.warn("Class attribute value for taxon not yet supported: " +
984 // taxon.addMarker(Marker.NewInstance(markerType, true));
987 // save(name, state);
988 // save(taxon, state);
997 private NonViralName
<?
> createNameByCode(MarkupImportState state
, Rank rank
) {
998 NonViralName
<?
> name
;
999 NomenclaturalCode nc
= makeNomenclaturalCode(state
);
1000 name
= (NonViralName
<?
>) nc
.getNewTaxonNameInstance(rank
);
1008 private NomenclaturalCode
makeNomenclaturalCode(MarkupImportState state
) {
1009 NomenclaturalCode nc
= state
.getConfig().getNomenclaturalCode();
1011 nc
= NomenclaturalCode
.ICBN
; // default;
1016 private String
handleTaxonTitle(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
1019 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
1020 String rankAttr
= getAndRemoveAttributeValue(attributes
, RANK
);
1021 Rank rank
= makeRank(state
, rankAttr
, false);
1022 String num
= getAndRemoveAttributeValue(attributes
, NUM
);
1023 state
.setCurrentTaxonNum(num
);
1024 checkNoAttributes(attributes
, parentEvent
);
1026 // TODO handle attributes
1027 while (reader
.hasNext()) {
1028 XMLEvent next
= readNoWhitespace(reader
);
1029 if (next
.isEndElement()) {
1030 if (isMyEndingElement(next
, parentEvent
)) {
1031 Taxon taxon
= state
.getCurrentTaxon();
1032 String titleText
= null;
1033 if (checkMandatoryText(text
, parentEvent
)) {
1034 titleText
= normalize(text
);
1035 UUID uuidTitle
= MarkupTransformer
.uuidTaxonTitle
;
1036 ExtensionType titleExtension
= this.getExtensionType(state
, uuidTitle
, "Taxon Title ","taxon title", "title");
1037 taxon
.addExtension(titleText
, titleExtension
);
1039 taxon
.getName().setRank(rank
);
1040 // TODO check title exists
1043 if (isEndingElement(next
, FOOTNOTE
)) {
1044 // NOT YET IMPLEMENTED
1045 popUnimplemented(next
.asEndElement());
1047 handleUnexpectedEndElement(next
.asEndElement());
1048 state
.setUnsuccessfull();
1051 } else if (next
.isStartElement()) {
1052 if (isStartingElement(next
, FOOTNOTE
)) {
1053 handleNotYetImplementedElement(next
);
1055 handleUnexpectedStartElement(next
);
1056 state
.setUnsuccessfull();
1058 } else if (next
.isCharacters()) {
1059 text
+= next
.asCharacters().getData();
1062 handleUnexpectedElement(next
);
1063 state
.setUnsuccessfull();
1070 private WriterDataHolder
handleWriter(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
1072 checkNoAttributes(parentEvent
);
1073 WriterDataHolder dataHolder
= new WriterDataHolder();
1074 List
<FootnoteDataHolder
> footnotes
= new ArrayList
<FootnoteDataHolder
>();
1076 // TODO handle attributes
1077 while (reader
.hasNext()) {
1078 XMLEvent next
= readNoWhitespace(reader
);
1079 if (isMyEndingElement(next
, parentEvent
)) {
1080 text
= CdmUtils
.removeBrackets(text
);
1081 if (checkMandatoryText(text
, parentEvent
)) {
1082 text
= normalize(text
);
1083 dataHolder
.writer
= text
;
1084 dataHolder
.footnotes
= footnotes
;
1087 UUID uuidWriterExtension
= MarkupTransformer
.uuidWriterExtension
;
1088 ExtensionType writerExtensionType
= this
1089 .getExtensionType(state
, uuidWriterExtension
,
1090 "Writer", "writer", "writer");
1091 Extension extension
= Extension
.NewInstance();
1092 extension
.setType(writerExtensionType
);
1093 extension
.setValue(text
);
1094 dataHolder
.extension
= extension
;
1097 UUID uuidWriterAnnotation
= MarkupTransformer
.uuidWriterAnnotation
;
1098 AnnotationType writerAnnotationType
= this.getAnnotationType(state
, uuidWriterAnnotation
, "Writer", "writer", "writer", null);
1099 Annotation annotation
= Annotation
.NewInstance(text
, writerAnnotationType
, Language
.DEFAULT());
1100 dataHolder
.annotation
= annotation
;
1106 } else if (isStartingElement(next
, FOOTNOTE_REF
)) {
1107 FootnoteDataHolder footNote
= handleFootnoteRef(state
, reader
, next
);
1108 if (footNote
.isRef()) {
1109 footnotes
.add(footNote
);
1111 logger
.warn("Non ref footnotes not yet impelemnted");
1113 } else if (next
.isCharacters()) {
1114 text
+= next
.asCharacters().getData();
1117 handleUnexpectedElement(next
);
1118 state
.setUnsuccessfull();
1121 throw new IllegalStateException("<writer> has no end tag");
1124 private void registerFootnotes(MarkupImportState state
, AnnotatableEntity entity
, List
<FootnoteDataHolder
> footnotes
) {
1125 for (FootnoteDataHolder footNote
: footnotes
) {
1126 registerFootnoteDemand(state
, entity
, footNote
);
1130 private void registerGivenFootnote(MarkupImportState state
, FootnoteDataHolder footnote
) {
1131 state
.registerFootnote(footnote
);
1132 Set
<AnnotatableEntity
> demands
= state
.getFootnoteDemands(footnote
.id
);
1133 if (demands
!= null) {
1134 for (AnnotatableEntity entity
: demands
) {
1135 attachFootnote(state
, entity
, footnote
);
1140 private void registerGivenFigure(MarkupImportState state
, String id
, Media figure
) {
1141 state
.registerFigure(id
, figure
);
1142 Set
<AnnotatableEntity
> demands
= state
.getFigureDemands(id
);
1143 if (demands
!= null) {
1144 for (AnnotatableEntity entity
: demands
) {
1145 attachFigure(state
, entity
, figure
);
1150 private void registerFootnoteDemand(MarkupImportState state
, AnnotatableEntity entity
, FootnoteDataHolder footnote
) {
1151 FootnoteDataHolder existingFootnote
= state
.getFootnote(footnote
.ref
);
1152 if (existingFootnote
!= null) {
1153 attachFootnote(state
, entity
, existingFootnote
);
1155 Set
<AnnotatableEntity
> demands
= state
.getFootnoteDemands(footnote
.ref
);
1156 if (demands
== null) {
1157 demands
= new HashSet
<AnnotatableEntity
>();
1158 state
.putFootnoteDemands(footnote
.ref
, demands
);
1160 demands
.add(entity
);
1164 private void registerFigureDemand(MarkupImportState state
, AnnotatableEntity entity
, String figureRef
) {
1165 Media existingFigure
= state
.getFigure(figureRef
);
1166 if (existingFigure
!= null) {
1167 attachFigure(state
, entity
, existingFigure
);
1169 Set
<AnnotatableEntity
> demands
= state
.getFigureDemands(figureRef
);
1170 if (demands
== null) {
1171 demands
= new HashSet
<AnnotatableEntity
>();
1172 state
.putFigureDemands(figureRef
, demands
);
1174 demands
.add(entity
);
1178 private void attachFootnote(MarkupImportState state
, AnnotatableEntity entity
, FootnoteDataHolder footnote
) {
1179 AnnotationType annotationType
= this.getAnnotationType(state
, MarkupTransformer
.uuidFootnote
, "Footnote", "An e-flora footnote", "fn", null);
1180 Annotation annotation
= Annotation
.NewInstance(footnote
.string
,
1181 annotationType
, Language
.DEFAULT());
1182 // TODO transient objects
1183 entity
.addAnnotation(annotation
);
1184 save(entity
, state
);
1187 private void attachFigure(MarkupImportState state
,
1188 AnnotatableEntity entity
, Media figure
) {
1189 // IdentifiableEntity<?> toSave;
1190 if (entity
.isInstanceOf(TextData
.class)) {
1191 TextData deb
= CdmBase
.deproxy(entity
, TextData
.class);
1192 deb
.addMedia(figure
);
1193 // toSave = ((TaxonDescription)deb.getInDescription()).getTaxon();
1194 } else if (entity
.isInstanceOf(IdentifiableMediaEntity
.class)) {
1195 IdentifiableMediaEntity
<?
> ime
= CdmBase
.deproxy(entity
,
1196 IdentifiableMediaEntity
.class);
1197 ime
.addMedia(figure
);
1200 String message
= "Unsupported entity to attach media: %s";
1201 message
= String
.format(message
, entity
.getClass().getName());
1204 save(entity
, state
);
1207 private void handleFigure(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
1208 // FigureDataHolder result = new FigureDataHolder();
1210 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
1211 String id
= getAndRemoveAttributeValue(attributes
, ID
);
1212 String type
= getAndRemoveAttributeValue(attributes
, TYPE
);
1213 checkNoAttributes(attributes
, parentEvent
);
1215 String urlString
= null;
1216 String legendString
= null;
1217 String titleString
= null;
1218 String numString
= null;
1220 while (reader
.hasNext()) {
1221 XMLEvent next
= readNoWhitespace(reader
);
1222 if (isMyEndingElement(next
, parentEvent
)) {
1223 makeFigure(state
, id
, type
, urlString
, legendString
, titleString
, numString
, next
);
1225 } else if (isStartingElement(next
, FIGURE_LEGEND
)) {
1226 // TODO same as figurestring ?
1227 legendString
= handleFootnoteString(state
, reader
, next
);
1228 } else if (isStartingElement(next
, FIGURE_TITLE
)) {
1229 titleString
= getCData(state
, reader
, next
);
1230 } else if (isStartingElement(next
, URL
)) {
1231 String localUrl
= getCData(state
, reader
, next
);
1232 urlString
= CdmUtils
.Nz(state
.getBaseMediaUrl()) + localUrl
;
1233 } else if (isStartingElement(next
, NUM
)) {
1234 numString
= getCData(state
, reader
, next
);
1235 } else if (next
.isCharacters()) {
1236 text
+= next
.asCharacters().getData();
1238 fireUnexpectedEvent(next
, 0);
1241 throw new IllegalStateException("<figure> has no end tag");
1249 * @param legendString
1250 * @param titleString
1254 private void makeFigure(MarkupImportState state
, String id
, String type
, String urlString
,
1255 String legendString
, String titleString
, String numString
, XMLEvent next
) {
1257 boolean isFigure
= false;
1259 //TODO maybe everything is a figure as it is all taken from a book
1260 if ("lineart".equals(type
)) {
1262 // media = Figure.NewInstance(url.toURI(), null, null, null);
1263 } else if (type
== null || "photo".equals(type
)
1264 || "signature".equals(type
)
1265 || "others".equals(type
)) {
1267 String message
= "Unknown figure type '%s'";
1268 message
= String
.format(message
, type
);
1269 fireWarningEvent(message
, next
, 2);
1271 media
= docImport
.getImageMedia(urlString
, docImport
.getReadMediaData(), isFigure
);
1275 if (StringUtils
.isNotBlank(titleString
)) {
1276 media
.putTitle(Language
.DEFAULT(), titleString
);
1279 if (StringUtils
.isNotBlank(legendString
)) {
1280 media
.addDescription(legendString
, Language
.DEFAULT());
1282 if (StringUtils
.isNotBlank(numString
)) {
1283 // TODO use concrete source (e.g. DAPHNIPHYLLACEAE in FM
1285 Reference
<?
> citation
= state
.getConfig().getSourceReference();
1286 media
.addSource(numString
, "num", citation
, null);
1287 // TODO name used in source if available
1289 // TODO which citation
1290 if (StringUtils
.isNotBlank(id
)) {
1291 media
.addSource(id
, null, state
.getConfig().getSourceReference(), null);
1293 String message
= "Figure id should never be empty or null";
1294 fireWarningEvent(message
, next
, 6);
1301 } catch (MalformedURLException e
) {
1302 String message
= "Media uri has incorrect syntax: %s";
1303 message
= String
.format(message
, urlString
);
1304 fireWarningEvent(message
, next
, 4);
1305 // } catch (URISyntaxException e) {
1306 // String message = "Media uri has incorrect syntax: %s";
1307 // message = String.format(message, urlString);
1308 // fireWarningEvent(message, next, 4);
1311 registerGivenFigure(state
, id
, media
);
1314 private FigureDataHolder
handleFigureRef(MarkupImportState state
,
1315 XMLEventReader reader
, XMLEvent parentEvent
)
1316 throws XMLStreamException
{
1317 FigureDataHolder result
= new FigureDataHolder();
1318 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
1319 result
.ref
= getAndRemoveAttributeValue(attributes
, REF
);
1320 checkNoAttributes(attributes
, parentEvent
);
1322 // text is not handled, needed only for debugging purposes
1324 while (reader
.hasNext()) {
1325 XMLEvent next
= readNoWhitespace(reader
);
1326 if (isMyEndingElement(next
, parentEvent
)) {
1328 } else if (isStartingElement(next
, NUM
)) {
1329 String num
= getCData(state
, reader
, next
);
1330 result
.num
= num
; // num is not handled during import
1331 } else if (isStartingElement(next
, FIGURE_PART
)) {
1332 result
.figurePart
= getCData(state
, reader
, next
);
1333 } else if (next
.isCharacters()) {
1334 text
+= next
.asCharacters().getData();
1336 fireUnexpectedEvent(next
, 0);
1339 throw new IllegalStateException("<figureRef> has no end tag");
1342 private FootnoteDataHolder
handleFootnote(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
1343 FootnoteDataHolder result
= new FootnoteDataHolder();
1344 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
1345 result
.id
= getAndRemoveAttributeValue(attributes
, ID
);
1346 // result.ref = getAndRemoveAttributeValue(attributes, REF);
1347 checkNoAttributes(attributes
, parentEvent
);
1349 while (reader
.hasNext()) {
1350 XMLEvent next
= readNoWhitespace(reader
);
1351 if (isStartingElement(next
, FOOTNOTE_STRING
)) {
1352 String string
= handleFootnoteString(state
, reader
, next
);
1353 result
.string
= string
;
1354 } else if (isMyEndingElement(next
, parentEvent
)) {
1357 fireUnexpectedEvent(next
, 0);
1363 private FootnoteDataHolder
handleFootnoteRef(MarkupImportState state
,
1364 XMLEventReader reader
, XMLEvent parentEvent
)
1365 throws XMLStreamException
{
1366 FootnoteDataHolder result
= new FootnoteDataHolder();
1367 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
1368 result
.ref
= getAndRemoveAttributeValue(attributes
, REF
);
1369 checkNoAttributes(attributes
, parentEvent
);
1371 // text is not handled, needed only for debugging purposes
1373 while (reader
.hasNext()) {
1374 XMLEvent next
= readNoWhitespace(reader
);
1375 // if (isStartingElement(next, FOOTNOTE_STRING)){
1376 // String string = handleFootnoteString(state, reader, next);
1377 // result.string = string;
1379 if (isMyEndingElement(next
, parentEvent
)) {
1381 } else if (next
.isCharacters()) {
1382 text
+= next
.asCharacters().getData();
1385 fireUnexpectedEvent(next
, 0);
1391 private void handleNomenclature(MarkupImportState state
,
1392 XMLEventReader reader
, XMLEvent parentEvent
)
1393 throws XMLStreamException
{
1394 checkNoAttributes(parentEvent
);
1396 while (reader
.hasNext()) {
1397 XMLEvent next
= readNoWhitespace(reader
);
1398 if (isStartingElement(next
, HOMOTYPES
)) {
1399 handleHomotypes(state
, reader
, next
.asStartElement());
1400 } else if (isMyEndingElement(next
, parentEvent
)) {
1403 fireSchemaConflictEventExpectedStartTag(HOMOTYPES
, reader
);
1404 state
.setUnsuccessfull();
1410 private String
handleFootnoteString(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
1411 boolean isTextMode
= true;
1413 while (reader
.hasNext()) {
1414 XMLEvent next
= readNoWhitespace(reader
);
1415 if (isMyEndingElement(next
, parentEvent
)) {
1417 } else if (next
.isEndElement()) {
1418 if (isEndingElement(next
, FULL_NAME
)) {
1419 popUnimplemented(next
.asEndElement());
1420 } else if (isEndingElement(next
, BR
)) {
1422 } else if (isHtml(next
)) {
1423 text
+= getXmlTag(next
);
1425 handleUnexpectedEndElement(next
.asEndElement());
1427 } else if (next
.isStartElement()) {
1428 if (isStartingElement(next
, FULL_NAME
)) {
1429 handleNotYetImplementedElement(next
);
1430 } else if (isStartingElement(next
, GATHERING
)) {
1431 text
+= handleInLineGathering(state
, reader
, next
);
1432 } else if (isStartingElement(next
, REFERENCES
)) {
1433 text
+= " " + handleInLineReferences(state
, reader
, next
)+ " ";
1434 } else if (isStartingElement(next
, BR
)) {
1437 } else if (isHtml(next
)) {
1438 text
+= getXmlTag(next
);
1440 handleUnexpectedStartElement(next
.asStartElement());
1442 } else if (next
.isCharacters()) {
1444 String message
= "footnoteString is not in text mode";
1445 fireWarningEvent(message
, next
, 6);
1447 text
+= next
.asCharacters().getData().trim();
1448 // getCData(state, reader, next); does not work as we have inner tags like <references>
1451 handleUnexpectedEndElement(next
.asEndElement());
1454 throw new IllegalStateException("<footnoteString> has no closing tag");
1458 private String
handleInLineGathering(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
1459 DerivedUnitFacade facade
= DerivedUnitFacade
.NewInstance(DerivedUnitType
.DerivedUnit
.FieldObservation
);
1460 handleGathering(state
, reader
, parentEvent
, facade
);
1461 FieldObservation fieldObservation
= facade
.innerFieldObservation();
1462 String result
= "<cdm:specimen uuid='%s'>%s</specimen>";
1463 result
= String
.format(result
, fieldObservation
.getUuid(), fieldObservation
.getTitleCache());
1464 save(fieldObservation
, state
);
1468 private String
handleInLineReferences(MarkupImportState state
,XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
1469 checkNoAttributes(parentEvent
);
1471 boolean hasReference
= false;
1473 while (reader
.hasNext()) {
1474 XMLEvent next
= readNoWhitespace(reader
);
1475 if (isMyEndingElement(next
, parentEvent
)) {
1476 checkMandatoryElement(hasReference
, parentEvent
.asStartElement(), REFERENCE
);
1478 } else if (isStartingElement(next
, REFERENCE
)) {
1479 text
+= handleInLineReference(state
, reader
, next
);
1480 hasReference
= true;
1482 handleUnexpectedElement(next
);
1485 throw new IllegalStateException("<References> has no closing tag");
1488 private String
handleInLineReference(MarkupImportState state
,XMLEventReader reader
, XMLEvent parentEvent
)throws XMLStreamException
{
1489 Reference
<?
> reference
= handleReference(state
, reader
, parentEvent
);
1490 String result
= "<cdm:ref uuid='%s'>%s</ref>";
1491 result
= String
.format(result
, reference
.getUuid(), reference
.getTitleCache());
1492 save(reference
, state
);
1496 private Reference
<?
> handleReference(MarkupImportState state
,XMLEventReader reader
, XMLEvent parentEvent
)throws XMLStreamException
{
1497 checkNoAttributes(parentEvent
);
1499 boolean hasRefPart
= false;
1500 Map
<String
, String
> refMap
= new HashMap
<String
, String
>();
1501 while (reader
.hasNext()) {
1502 XMLEvent next
= readNoWhitespace(reader
);
1503 if (isMyEndingElement(next
, parentEvent
)) {
1504 checkMandatoryElement(hasRefPart
, parentEvent
.asStartElement(),
1506 Reference
<?
> reference
= createReference(state
, refMap
, next
);
1508 } else if (isStartingElement(next
, REF_PART
)) {
1509 handleRefPart(state
, reader
, next
, refMap
);
1512 handleUnexpectedElement(next
);
1515 // TODO handle missing end element
1516 throw new IllegalStateException("<Reference> has no closing tag");
1519 private void handleHomotypes(MarkupImportState state
, XMLEventReader reader
, StartElement parentEvent
)
1520 throws XMLStreamException
{
1521 checkNoAttributes(parentEvent
);
1523 HomotypicalGroup homotypicalGroup
= null;
1525 boolean hasNom
= false;
1526 while (reader
.hasNext()) {
1527 XMLEvent next
= readNoWhitespace(reader
);
1528 if (next
.isEndElement()) {
1529 if (isMyEndingElement(next
, parentEvent
)) {
1530 checkMandatoryElement(hasNom
, parentEvent
, NOM
);
1533 if (isEndingElement(next
, NAME_TYPE
)) {
1534 state
.setNameType(false);
1535 } else if (isEndingElement(next
, NOTES
)) {
1536 // NOT YET IMPLEMENTED
1537 popUnimplemented(next
.asEndElement());
1539 handleUnexpectedEndElement(next
.asEndElement());
1542 } else if (next
.isStartElement()) {
1543 if (isStartingElement(next
, NOM
)) {
1544 NonViralName
<?
> name
= handleNom(state
, reader
, next
, homotypicalGroup
);
1545 homotypicalGroup
= name
.getHomotypicalGroup();
1547 } else if (isStartingElement(next
, NAME_TYPE
)) {
1548 state
.setNameType(true);
1549 handleNameType(state
, reader
, next
, homotypicalGroup
);
1550 } else if (isStartingElement(next
, SPECIMEN_TYPE
)) {
1551 handleSpecimenType(state
, reader
, next
, homotypicalGroup
);
1552 } else if (isStartingElement(next
, NOTES
)) {
1553 handleNotYetImplementedElement(next
);
1555 handleUnexpectedStartElement(next
);
1558 handleUnexpectedElement(next
);
1561 // TODO handle missing end element
1562 throw new IllegalStateException("Homotypes has no closing tag");
1566 private void handleNameType(MarkupImportState state
, XMLEventReader reader
,
1567 XMLEvent parentEvent
, HomotypicalGroup homotypicalGroup
)
1568 throws XMLStreamException
{
1569 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
1570 String typeStatus
= getAndRemoveAttributeValue(attributes
, TYPE_STATUS
);
1571 checkNoAttributes(attributes
, parentEvent
);
1573 NameTypeDesignationStatus status
;
1575 status
= NameTypeParser
.parseNameTypeStatus(typeStatus
);
1576 } catch (UnknownCdmTypeException e
) {
1577 String message
= "Type status could not be recognized: %s";
1578 message
= String
.format(message
, typeStatus
);
1579 fireWarningEvent(message
, parentEvent
, 4);
1583 boolean hasNom
= false;
1584 while (reader
.hasNext()) {
1585 XMLEvent next
= readNoWhitespace(reader
);
1586 if (next
.isEndElement()) {
1587 if (isMyEndingElement(next
, parentEvent
)) {
1588 checkMandatoryElement(hasNom
, parentEvent
.asStartElement(),
1590 state
.setNameType(false);
1593 if (isEndingElement(next
, ACCEPTED_NAME
)) {
1594 // NOT YET IMPLEMENTED
1595 popUnimplemented(next
.asEndElement());
1597 handleUnexpectedEndElement(next
.asEndElement());
1600 } else if (next
.isStartElement()) {
1601 if (isStartingElement(next
, NOM
)) {
1602 // TODO should we check if the type is always a species, is
1604 NonViralName
<?
> speciesName
= handleNom(state
, reader
,
1606 for (TaxonNameBase
<?
, ?
> name
: homotypicalGroup
1607 .getTypifiedNames()) {
1608 name
.addNameTypeDesignation(speciesName
, null, null,
1609 null, status
, false, false, false, false);
1612 } else if (isStartingElement(next
, ACCEPTED_NAME
)) {
1613 handleNotYetImplementedElement(next
);
1615 handleUnexpectedStartElement(next
);
1618 handleUnexpectedElement(next
);
1621 // TODO handle missing end element
1622 throw new IllegalStateException("Homotypes has no closing tag");
1626 private void handleSpecimenType(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
,
1627 HomotypicalGroup homotypicalGroup
) throws XMLStreamException
{
1629 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
1630 String typeStatus
= getAndRemoveAttributeValue(attributes
, TYPE_STATUS
);
1631 String notSeen
= getAndRemoveAttributeValue(attributes
, NOT_SEEN
);
1632 String unknown
= getAndRemoveAttributeValue(attributes
, UNKNOWN
);
1633 String notFound
= getAndRemoveAttributeValue(attributes
, NOT_FOUND
);
1634 String destroyed
= getAndRemoveAttributeValue(attributes
, DESTROYED
);
1635 String lost
= getAndRemoveAttributeValue(attributes
, LOST
);
1636 checkNoAttributes(attributes
, parentEvent
);
1637 if (StringUtils
.isNotEmpty(typeStatus
)) {
1639 // currently not needed
1640 } else if (StringUtils
.isNotEmpty(notSeen
)) {
1641 handleNotYetImplementedAttribute(attributes
, NOT_SEEN
);
1642 } else if (StringUtils
.isNotEmpty(unknown
)) {
1643 handleNotYetImplementedAttribute(attributes
, UNKNOWN
);
1644 } else if (StringUtils
.isNotEmpty(notFound
)) {
1645 handleNotYetImplementedAttribute(attributes
, NOT_FOUND
);
1646 } else if (StringUtils
.isNotEmpty(destroyed
)) {
1647 handleNotYetImplementedAttribute(attributes
, DESTROYED
);
1648 } else if (StringUtils
.isNotEmpty(lost
)) {
1649 handleNotYetImplementedAttribute(attributes
, LOST
);
1652 NonViralName
<?
> firstName
= null;
1653 Set
<TaxonNameBase
> names
= homotypicalGroup
.getTypifiedNames();
1654 if (names
.isEmpty()) {
1655 String message
= "There is no name in a homotypical group. Can't create the specimen type";
1656 fireWarningEvent(message
, parentEvent
, 8);
1658 firstName
= CdmBase
.deproxy(names
.iterator().next(),NonViralName
.class);
1661 DerivedUnitFacade facade
= DerivedUnitFacade
.NewInstance(DerivedUnitType
.Specimen
);
1664 while (reader
.hasNext()) {
1665 XMLEvent next
= readNoWhitespace(reader
);
1666 if (next
.isEndElement()) {
1667 if (isMyEndingElement(next
, parentEvent
)) {
1668 makeSpecimenType(state
, facade
, text
, firstName
, parentEvent
);
1671 if (isEndingElement(next
, FULL_TYPE
)) {
1672 // NOT YET IMPLEMENTED
1673 popUnimplemented(next
.asEndElement());
1674 } else if (isEndingElement(next
, TYPE_STATUS
)) {
1675 // NOT YET IMPLEMENTED
1676 popUnimplemented(next
.asEndElement());
1677 } else if (isEndingElement(next
, ORIGINAL_DETERMINATION
)) {
1678 // NOT YET IMPLEMENTED
1679 popUnimplemented(next
.asEndElement());
1680 } else if (isEndingElement(next
, SPECIMEN_TYPE
)) {
1681 // NOT YET IMPLEMENTED
1682 popUnimplemented(next
.asEndElement());
1683 } else if (isEndingElement(next
, COLLECTION_AND_TYPE
)) {
1684 // NOT YET IMPLEMENTED
1685 popUnimplemented(next
.asEndElement());
1686 } else if (isEndingElement(next
, CITATION
)) {
1687 // NOT YET IMPLEMENTED
1688 popUnimplemented(next
.asEndElement());
1689 } else if (isEndingElement(next
, NOTES
)) {
1690 // NOT YET IMPLEMENTED
1691 popUnimplemented(next
.asEndElement());
1692 } else if (isEndingElement(next
, ANNOTATION
)) {
1693 // NOT YET IMPLEMENTED
1694 popUnimplemented(next
.asEndElement());
1696 handleUnexpectedEndElement(next
.asEndElement());
1699 } else if (next
.isStartElement()) {
1700 if (isStartingElement(next
, FULL_TYPE
)) {
1701 handleNotYetImplementedElement(next
);
1702 // homotypicalGroup = handleNom(state, reader, next, taxon,
1703 // homotypicalGroup);
1704 } else if (isStartingElement(next
, TYPE_STATUS
)) {
1705 handleNotYetImplementedElement(next
);
1706 } else if (isStartingElement(next
, GATHERING
)) {
1707 handleGathering(state
, reader
, next
, facade
);
1708 } else if (isStartingElement(next
, ORIGINAL_DETERMINATION
)) {
1709 handleNotYetImplementedElement(next
);
1710 } else if (isStartingElement(next
, SPECIMEN_TYPE
)) {
1711 handleNotYetImplementedElement(next
);
1712 } else if (isStartingElement(next
, COLLECTION_AND_TYPE
)) {
1713 handleNotYetImplementedElement(next
);
1714 } else if (isStartingElement(next
, CITATION
)) {
1715 handleNotYetImplementedElement(next
);
1716 } else if (isStartingElement(next
, NOTES
)) {
1717 handleNotYetImplementedElement(next
);
1718 } else if (isStartingElement(next
, ANNOTATION
)) {
1719 handleNotYetImplementedElement(next
);
1721 handleUnexpectedStartElement(next
);
1723 } else if (next
.isCharacters()) {
1724 text
+= next
.asCharacters().getData();
1726 handleUnexpectedElement(next
);
1729 // TODO handle missing end element
1730 throw new IllegalStateException("Specimen type has no closing tag");
1733 private void makeSpecimenType(MarkupImportState state
, DerivedUnitFacade facade
, String text
,
1734 NonViralName name
, XMLEvent parentEvent
) {
1737 if (text
.matches("^\\(.*\\)\\.?$")) {
1738 text
= text
.replaceAll("\\.", "");
1739 text
= text
.substring(1, text
.length() - 1);
1741 String
[] split
= text
.split("[;,]");
1742 for (String str
: split
) {
1744 boolean addToAllNamesInGroup
= true;
1745 TypeInfo typeInfo
= makeSpecimenTypeTypeInfo(str
, parentEvent
);
1746 SpecimenTypeDesignationStatus typeStatus
= typeInfo
.status
;
1747 Collection collection
= createCollection(typeInfo
.collectionString
);
1749 // TODO improve cache strategy handling
1750 DerivedUnitBase typeSpecimen
= facade
.addDuplicate(collection
,
1751 null, null, null, null);
1752 typeSpecimen
.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
1753 name
.addSpecimenTypeDesignation((Specimen
) typeSpecimen
, typeStatus
, null, null, null, false, addToAllNamesInGroup
);
1757 private Collection
createCollection(String code
) {
1759 // TODO code <-> name
1760 Collection result
= Collection
.NewInstance();
1761 result
.setCode(code
);
1765 private TypeInfo
makeSpecimenTypeTypeInfo(String originalString
, XMLEvent event
) {
1766 TypeInfo result
= new TypeInfo();
1767 String
[] split
= originalString
.split("\\s+");
1768 for (String str
: split
) {
1769 if (str
.matches(SpecimenTypeParser
.typeTypePattern
)) {
1770 SpecimenTypeDesignationStatus status
;
1772 status
= SpecimenTypeParser
.parseSpecimenTypeStatus(str
);
1773 } catch (UnknownCdmTypeException e
) {
1774 String message
= "Specimen type status '%s' not recognized by parser";
1775 message
= String
.format(message
, str
);
1776 fireWarningEvent(message
, event
, 4);
1779 result
.status
= status
;
1780 } else if (str
.matches(SpecimenTypeParser
.collectionPattern
)) {
1781 result
.collectionString
= str
;
1783 String message
= "Type part '%s' could not be recognized";
1784 message
= String
.format(message
, str
);
1785 fireWarningEvent(message
, event
, 2);
1793 private void handleGathering(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
, DerivedUnitFacade facade
) throws XMLStreamException
{
1794 checkNoAttributes(parentEvent
);
1795 boolean hasCollector
= false;
1796 boolean hasFieldNum
= false;
1799 while (reader
.hasNext()) {
1800 XMLEvent next
= readNoWhitespace(reader
);
1801 if (next
.isEndElement()) {
1802 if (isMyEndingElement(next
, parentEvent
)) {
1803 checkMandatoryElement(hasCollector
,parentEvent
.asStartElement(), COLLECTOR
);
1804 checkMandatoryElement(hasFieldNum
,parentEvent
.asStartElement(), FIELD_NUM
);
1807 if (isEndingElement(next
, ALTERNATIVE_COLLECTOR
)) {
1808 // NOT YET IMPLEMENTED
1809 popUnimplemented(next
.asEndElement());
1810 } else if (isEndingElement(next
, ALTERNATIVE_FIELD_NUM
)) {
1811 // NOT YET IMPLEMENTED
1812 popUnimplemented(next
.asEndElement());
1813 } else if (isEndingElement(next
, COLLECTION_TYPE_STATUS
)) {
1814 // NOT YET IMPLEMENTED
1815 popUnimplemented(next
.asEndElement());
1816 } else if (isEndingElement(next
, COLLECTION_AND_TYPE
)) {
1817 // NOT YET IMPLEMENTED , does this make sense here?
1818 popUnimplemented(next
.asEndElement());
1819 } else if (isEndingElement(next
,
1820 ALTERNATIVE_COLLECTION_TYPE_STATUS
)) {
1821 // NOT YET IMPLEMENTED
1822 popUnimplemented(next
.asEndElement());
1823 } else if (isEndingElement(next
, SUB_COLLECTION
)) {
1824 // NOT YET IMPLEMENTED
1825 popUnimplemented(next
.asEndElement());
1826 } else if (isEndingElement(next
, COLLECTION
)) {
1827 // NOT YET IMPLEMENTED
1828 popUnimplemented(next
.asEndElement());
1829 } else if (isEndingElement(next
, DATES
)) {
1830 // NOT YET IMPLEMENTED
1831 popUnimplemented(next
.asEndElement());
1832 } else if (isEndingElement(next
, NOTES
)) {
1833 // NOT YET IMPLEMENTED
1834 popUnimplemented(next
.asEndElement());
1836 handleUnexpectedEndElement(next
.asEndElement());
1839 } else if (next
.isStartElement()) {
1840 if (isStartingElement(next
, COLLECTOR
)) {
1841 hasCollector
= true;
1842 String collectorStr
= getCData(state
, reader
, next
);
1843 AgentBase
<?
> collector
= createCollector(collectorStr
);
1844 facade
.setCollector(collector
);
1845 } else if (isStartingElement(next
, ALTERNATIVE_COLLECTOR
)) {
1846 handleNotYetImplementedElement(next
);
1847 } else if (isStartingElement(next
, FIELD_NUM
)) {
1849 String fieldNumStr
= getCData(state
, reader
, next
);
1850 facade
.setFieldNumber(fieldNumStr
);
1851 } else if (isStartingElement(next
, ALTERNATIVE_FIELD_NUM
)) {
1852 handleNotYetImplementedElement(next
);
1853 } else if (isStartingElement(next
, COLLECTION_TYPE_STATUS
)) {
1854 handleNotYetImplementedElement(next
);
1855 } else if (isStartingElement(next
, COLLECTION_AND_TYPE
)) { //does this make sense here?
1856 handleNotYetImplementedElement(next
);
1857 } else if (isStartingElement(next
, ALTERNATIVE_COLLECTION_TYPE_STATUS
)) {
1858 handleNotYetImplementedElement(next
);
1859 } else if (isStartingElement(next
, SUB_COLLECTION
)) {
1860 handleNotYetImplementedElement(next
);
1861 } else if (isStartingElement(next
, COLLECTION
)) {
1862 handleNotYetImplementedElement(next
);
1863 } else if (isStartingElement(next
, LOCALITY
)) {
1864 handleLocality(state
, reader
, next
, facade
);
1865 } else if (isStartingElement(next
, DATES
)) {
1866 handleNotYetImplementedElement(next
);
1867 } else if (isStartingElement(next
, NOTES
)) {
1868 handleNotYetImplementedElement(next
);
1870 handleUnexpectedStartElement(next
);
1873 handleUnexpectedElement(next
);
1876 // TODO handle missing end element
1877 throw new IllegalStateException("Collection has no closing tag");
1881 private void handleLocality(MarkupImportState state
, XMLEventReader reader
,XMLEvent parentEvent
, DerivedUnitFacade facade
)throws XMLStreamException
{
1882 String classValue
= getClassOnlyAttribute(parentEvent
);
1883 boolean isLocality
= false;
1884 NamedAreaLevel areaLevel
= null;
1885 if ("locality".equalsIgnoreCase(classValue
)) {
1888 areaLevel
= makeNamedAreaLevel(state
, classValue
, parentEvent
);
1893 while (reader
.hasNext()) {
1894 XMLEvent next
= readNoWhitespace(reader
);
1895 if (next
.isEndElement()) {
1896 if (isMyEndingElement(next
, parentEvent
)) {
1897 if (StringUtils
.isNotBlank(text
)) {
1898 text
= normalize(text
);
1900 facade
.setLocality(text
);
1902 text
= CdmUtils
.removeTrailingDot(text
);
1903 NamedArea area
= makeArea(state
, text
, areaLevel
);
1904 facade
.addCollectingArea(area
);
1910 if (isEndingElement(next
, ALTITUDE
)) {
1911 // NOT YET IMPLEMENTED
1912 popUnimplemented(next
.asEndElement());
1913 } else if (isEndingElement(next
, COORDINATES
)) {
1914 // NOT YET IMPLEMENTED
1915 popUnimplemented(next
.asEndElement());
1916 } else if (isEndingElement(next
, ANNOTATION
)) {
1917 // NOT YET IMPLEMENTED
1918 popUnimplemented(next
.asEndElement());
1920 handleUnexpectedEndElement(next
.asEndElement());
1923 } else if (next
.isStartElement()) {
1924 if (isStartingElement(next
, ALTITUDE
)) {
1925 handleNotYetImplementedElement(next
);
1926 // homotypicalGroup = handleNom(state, reader, next, taxon,
1927 // homotypicalGroup);
1928 } else if (isStartingElement(next
, COORDINATES
)) {
1929 handleNotYetImplementedElement(next
);
1930 } else if (isStartingElement(next
, ANNOTATION
)) {
1931 handleNotYetImplementedElement(next
);
1933 handleUnexpectedStartElement(next
);
1935 } else if (next
.isCharacters()) {
1936 text
+= next
.asCharacters().getData();
1938 handleUnexpectedElement(next
);
1941 throw new IllegalStateException("<SpecimenType> has no closing tag");
1944 // private NamedArea createArea(String text, NamedAreaLevel areaLevel, MarkupImportState state) {
1945 // NamedArea area = NamedArea.NewInstance(text, text, null);
1946 // area.setLevel(areaLevel);
1947 // save(area, state);
1951 private AgentBase
<?
> createCollector(String collectorStr
) {
1952 return createAuthor(collectorStr
);
1955 private String
getCData(MarkupImportState state
, XMLEventReader reader
, XMLEvent next
) throws XMLStreamException
{
1956 return getCData(state
, reader
, next
, true);
1960 * Reads character data. Any element other than character data or the ending
1961 * tag will fire an unexpected element event.
1967 * @throws XMLStreamException
1969 private String
getCData(MarkupImportState state
, XMLEventReader reader
, XMLEvent next
,boolean checkAttributes
) throws XMLStreamException
{
1970 if (checkAttributes
){
1971 checkNoAttributes(next
);
1975 while (reader
.hasNext()) {
1976 XMLEvent myNext
= readNoWhitespace(reader
);
1977 if (isMyEndingElement(myNext
, next
)) {
1979 } else if (myNext
.isCharacters()) {
1980 text
+= myNext
.asCharacters().getData();
1982 handleUnexpectedElement(myNext
);
1985 throw new IllegalStateException("Event has no closing tag");
1990 * Creates the name defined by a nom tag. Adds it to the given homotypical
1991 * group (if not null).
1995 * @param parentEvent
1996 * @param homotypicalGroup
1998 * @throws XMLStreamException
2000 private NonViralName
<?
> handleNom(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
,
2001 HomotypicalGroup homotypicalGroup
) throws XMLStreamException
{
2002 boolean isSynonym
= false;
2003 boolean isNameType
= state
.isNameType();
2005 String classValue
= getClassOnlyAttribute(parentEvent
);
2006 NonViralName
<?
> name
;
2007 if (!isNameType
&& ACCEPTED
.equalsIgnoreCase(classValue
)) {
2009 name
= createName(state
, homotypicalGroup
, isSynonym
);
2010 } else if (!isNameType
&& SYNONYM
.equalsIgnoreCase(classValue
)) {
2012 name
= createName(state
, homotypicalGroup
, isSynonym
);
2013 } else if (isNameType
&& NAME_TYPE
.equalsIgnoreCase(classValue
)) {
2014 // TODO do we need to define the rank here?
2015 name
= createNameByCode(state
, null);
2017 fireUnexpectedAttributeValue(parentEvent
, CLASS
, classValue
);
2018 name
= createNameByCode(state
, null);
2021 Map
<String
, String
> nameMap
= new HashMap
<String
, String
>();
2023 while (reader
.hasNext()) {
2024 XMLEvent next
= readNoWhitespace(reader
);
2025 if (next
.isEndElement()) {
2026 if (isMyEndingElement(next
, parentEvent
)) {
2027 // fill the name with all data gathered
2028 fillName(state
, nameMap
, name
, next
);
2031 if (isEndingElement(next
, FULL_NAME
)) {
2032 // NOT YET IMPLEMENTED
2033 popUnimplemented(next
.asEndElement());
2034 } else if (isEndingElement(next
, HOMONYM
)) {
2035 // NOT YET IMPLEMENTED
2036 popUnimplemented(next
.asEndElement());
2037 } else if (isEndingElement(next
, NOTES
)) {
2038 // NOT YET IMPLEMENTED
2039 popUnimplemented(next
.asEndElement());
2040 } else if (isEndingElement(next
, ANNOTATION
)) {
2041 // NOT YET IMPLEMENTED
2042 popUnimplemented(next
.asEndElement());
2044 handleUnexpectedEndElement(next
.asEndElement());
2047 } else if (next
.isStartElement()) {
2048 if (isStartingElement(next
, FULL_NAME
)) {
2049 handleNotYetImplementedElement(next
);
2050 // homotypicalGroup = handleNom(state, reader, next, taxon,
2051 // homotypicalGroup);
2052 } else if (isStartingElement(next
, NUM
)) {
2053 String num
= getCData(state
, reader
, next
);
2054 num
= num
.replace(".", "");
2055 num
= num
.replace(")", "");
2056 if (StringUtils
.isNotBlank(num
)){
2057 if (state
.getCurrentTaxonNum() != null && ! state
.getCurrentTaxonNum().equals(num
) ){
2058 String message
= "Taxontitle num and homotypes/nom/num differ ( %s <-> %s ). I use the later one.";
2059 message
= String
.format(message
, state
.getCurrentTaxonNum(), num
);
2060 fireWarningEvent(message
, next
, 4);
2062 state
.setCurrentTaxonNum(num
);
2064 } else if (isStartingElement(next
, NAME
)) {
2065 handleName(state
, reader
, next
, nameMap
);
2066 } else if (isStartingElement(next
, CITATION
)) {
2067 handleCitation(state
, reader
, next
, name
);
2068 } else if (isStartingElement(next
, HOMONYM
)) {
2069 handleNotYetImplementedElement(next
);
2070 } else if (isStartingElement(next
, NOTES
)) {
2071 handleNotYetImplementedElement(next
);
2072 } else if (isStartingElement(next
, ANNOTATION
)) {
2073 handleNotYetImplementedElement(next
);
2075 handleUnexpectedStartElement(next
);
2078 handleUnexpectedElement(next
);
2081 // TODO handle missing end element
2082 throw new IllegalStateException("Nom has no closing tag");
2086 private void fillName(MarkupImportState state
, Map
<String
, String
> nameMap
,
2087 NonViralName name
, XMLEvent event
) {
2089 // Ranks: family, subfamily, tribus, genus, subgenus, section,
2090 // subsection, species, subspecies, variety, subvariety, forma
2091 // infrank, paraut, author, infrparaut, infraut, status, notes
2093 String infrank
= getAndRemoveMapKey(nameMap
, INFRANK
);
2094 String authorStr
= getAndRemoveMapKey(nameMap
, AUTHOR
);
2095 String paraut
= getAndRemoveMapKey(nameMap
, PARAUT
);
2097 String infrParAut
= getAndRemoveMapKey(nameMap
, INFRPARAUT
);
2098 String infrAut
= getAndRemoveMapKey(nameMap
, INFRAUT
);
2100 String statusStr
= getAndRemoveMapKey(nameMap
, STATUS
);
2101 String notes
= getAndRemoveMapKey(nameMap
, NOTES
);
2103 makeRankDecision(state
, nameMap
, name
, event
, infrank
);
2105 // test consistency of rank and authors
2106 testRankAuthorConsistency(name
, event
, authorStr
, paraut
, infrParAut
,infrAut
);
2109 makeNomenclaturalAuthors(name
, event
, authorStr
, paraut
, infrParAut
,infrAut
);
2112 // TODO handle pro parte, pro syn. etc.
2113 if (StringUtils
.isNotBlank(statusStr
)) {
2114 String proPartePattern
= "(pro parte|p.p.)";
2115 if (statusStr
.matches(proPartePattern
)) {
2116 state
.setProParte(true);
2119 // TODO handle trim earlier
2120 statusStr
= statusStr
.trim();
2121 NomenclaturalStatusType nomStatusType
= NomenclaturalStatusType
.getNomenclaturalStatusTypeByAbbreviation(statusStr
);
2122 name
.addStatus(NomenclaturalStatus
.NewInstance(nomStatusType
));
2123 } catch (UnknownCdmTypeException e
) {
2124 String message
= "Status '%s' could not be recognized";
2125 message
= String
.format(message
, statusStr
);
2126 fireWarningEvent(message
, event
, 4);
2131 if (StringUtils
.isNotBlank(notes
)) {
2132 handleNotYetImplementedAttributeValue(event
, CLASS
, NOTES
);
2145 private void makeRankDecision(MarkupImportState state
,
2146 Map
<String
, String
> nameMap
, NonViralName
<?
> name
, XMLEvent event
,
2147 String infrankStr
) {
2149 for (String key
: nameMap
.keySet()) {
2150 Rank rank
= makeRank(state
, key
, false);
2152 handleNotYetImplementedAttributeValue(event
, CLASS
, key
);
2154 if (name
.getRank() == null || rank
.isLower(name
.getRank())) {
2157 String value
= nameMap
.get(key
);
2158 if (rank
.isSupraGeneric() || rank
.isGenus()) {
2159 name
.setGenusOrUninomial(toFirstCapital(value
));
2160 } else if (rank
.isInfraGeneric()) {
2161 name
.setInfraGenericEpithet(toFirstCapital(value
));
2162 } else if (rank
.isSpecies()) {
2163 name
.setSpecificEpithet(value
.toLowerCase());
2164 } else if (rank
.isInfraSpecific()) {
2165 name
.setInfraSpecificEpithet(value
.toLowerCase());
2167 String message
= "Invalid rank '%s'. Can't decide which epithet to fill with '%s'";
2168 message
= String
.format(message
, rank
.getTitleCache(),value
);
2169 fireWarningEvent(message
, event
, 4);
2174 // handle given infrank marker
2175 if (StringUtils
.isNotBlank(infrankStr
)) {
2176 Rank infRank
= makeRank(state
, infrankStr
, true);
2178 if (infRank
== null) {
2179 String message
= "Infrank '%s' rank not recognized";
2180 message
= String
.format(message
, infrankStr
);
2181 fireWarningEvent(message
, event
, 4);
2183 if (name
.getRank() == null) {
2184 name
.setRank(infRank
);
2185 } else if (infRank
.isLower(name
.getRank())) {
2186 String message
= "InfRank '%s' is lower than existing rank ";
2187 message
= String
.format(message
, infrankStr
);
2188 fireWarningEvent(message
, event
, 2);
2189 name
.setRank(infRank
);
2190 } else if (infRank
.equals(name
.getRank())) {
2193 String message
= "InfRank '%s' is higher than existing rank ";
2194 message
= String
.format(message
, infrankStr
);
2195 fireWarningEvent(message
, event
, 2);
2201 private String
toFirstCapital(String value
) {
2202 if (StringUtils
.isBlank(value
)){
2206 result
+= value
.substring(0,1).toUpperCase();
2207 if (value
.length()>1){
2208 result
+= value
.substring(1).toLowerCase();
2222 private void makeNomenclaturalAuthors(NonViralName name
, XMLEvent event
,
2223 String authorStr
, String paraut
, String infrParAut
, String infrAut
) {
2224 if (name
.getRank() != null && name
.getRank().isInfraSpecific()) {
2225 if (StringUtils
.isNotBlank(infrAut
)) {
2226 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(infrAut
, event
);
2227 name
.setCombinationAuthorTeam(authorAndEx
[0]);
2228 name
.setExCombinationAuthorTeam(authorAndEx
[1]);
2230 if (StringUtils
.isNotBlank(infrParAut
)) {
2231 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(infrParAut
, event
);
2232 name
.setBasionymAuthorTeam(authorAndEx
[0]);
2233 name
.setExBasionymAuthorTeam(authorAndEx
[1]);
2236 if (name
.getRank() == null){
2237 String message
= "No rank defined. Check correct usage of authors!";
2238 fireWarningEvent(message
, event
, 4);
2239 if (isNotBlank(infrParAut
) || isNotBlank(infrAut
)){
2240 authorStr
= infrAut
;
2241 paraut
= infrParAut
;
2244 if (StringUtils
.isNotBlank(authorStr
)) {
2245 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(authorStr
, event
);
2246 name
.setCombinationAuthorTeam(authorAndEx
[0]);
2247 name
.setExCombinationAuthorTeam(authorAndEx
[1]);
2249 if (StringUtils
.isNotBlank(paraut
)) {
2250 INomenclaturalAuthor
[] authorAndEx
= authorAndEx(paraut
, event
);
2251 name
.setBasionymAuthorTeam(authorAndEx
[0]);
2252 name
.setExBasionymAuthorTeam(authorAndEx
[1]);
2257 private TeamOrPersonBase
[] authorAndEx(String authorAndEx
, XMLEvent xmlEvent
) {
2258 authorAndEx
= authorAndEx
.trim();
2259 TeamOrPersonBase
[] result
= new TeamOrPersonBase
[2];
2261 String
[] split
= authorAndEx
.split("\\sex\\s");
2262 if (split
.length
> 2) {
2263 String message
= "There is more then 1 ' ex ' in author string. Can't separate author and ex-author";
2264 fireWarningEvent(message
, xmlEvent
, 4);
2265 result
[0] = createAuthor(authorAndEx
);
2266 } else if (split
.length
== 2) {
2267 result
[0] = createAuthor(split
[1]);
2268 result
[1] = createAuthor(split
[0]);
2270 result
[0] = createAuthor(split
[0]);
2276 * Tests if the names rank is consistent with the given author strings.
2284 private void testRankAuthorConsistency(NonViralName name
, XMLEvent event
,
2285 String authorStr
, String paraut
, String infrParAut
, String infrAut
) {
2286 if (name
.getRank() == null){
2289 if (name
.getRank().isInfraSpecific()) {
2290 if (StringUtils
.isBlank(infrParAut
)
2291 && StringUtils
.isBlank(infrAut
) //was isNotBlank before 29.5.2012
2292 && (StringUtils
.isNotBlank(paraut
) || StringUtils
.isNotBlank(authorStr
))
2293 && ! name
.isAutonym()) {
2294 String message
= "Rank is infraspecicific but has only specific or higher author(s)";
2295 fireWarningEvent(message
, event
, 4);
2298 // is not infraspecific
2299 if (StringUtils
.isNotBlank(infrParAut
) || StringUtils
.isNotBlank(infrAut
)) {
2300 String message
= "Rank is not infraspecicific but name has infra author(s)";
2301 fireWarningEvent(message
, event
, 4);
2307 * Returns the (empty) name with the correct homotypical group depending on
2308 * the taxon status. Throws NPE if no currentTaxon is set in state.
2311 * @param homotypicalGroup
2315 private NonViralName
<?
> createName(MarkupImportState state
,
2316 HomotypicalGroup homotypicalGroup
, boolean isSynonym
) {
2317 NonViralName
<?
> name
;
2318 Taxon taxon
= state
.getCurrentTaxon();
2320 Rank defaultRank
= Rank
.SPECIES(); // can be any
2321 name
= createNameByCode(state
, defaultRank
);
2322 if (homotypicalGroup
!= null) {
2323 name
.setHomotypicalGroup(homotypicalGroup
);
2325 SynonymRelationshipType synonymType
= SynonymRelationshipType
.HETEROTYPIC_SYNONYM_OF();
2326 if (taxon
.getHomotypicGroup().equals(homotypicalGroup
)) {
2327 synonymType
= SynonymRelationshipType
.HOMOTYPIC_SYNONYM_OF();
2329 taxon
.addSynonymName(name
, synonymType
);
2331 name
= CdmBase
.deproxy(taxon
.getName(), NonViralName
.class);
2336 private void handleName(MarkupImportState state
, XMLEventReader reader
,
2337 XMLEvent parentEvent
, Map
<String
, String
> nameMap
)
2338 throws XMLStreamException
{
2339 String classValue
= getClassOnlyAttribute(parentEvent
);
2342 while (reader
.hasNext()) {
2343 XMLEvent next
= readNoWhitespace(reader
);
2344 if (isMyEndingElement(next
, parentEvent
)) {
2345 nameMap
.put(classValue
, text
);
2347 } else if (next
.isStartElement()) {
2348 if (isStartingElement(next
, ANNOTATION
)) {
2349 handleNotYetImplementedElement(next
);
2351 handleUnexpectedStartElement(next
.asStartElement());
2353 } else if (next
.isCharacters()) {
2354 text
+= next
.asCharacters().getData();
2356 handleUnexpectedEndElement(next
.asEndElement());
2359 throw new IllegalStateException("name has no closing tag");
2369 private Rank
makeRank(MarkupImportState state
, String value
,
2372 if (StringUtils
.isBlank(value
)) {
2376 boolean useUnknown
= true;
2377 NomenclaturalCode nc
= makeNomenclaturalCode(state
);
2379 rank
= Rank
.getRankByAbbreviation(value
, nc
, useUnknown
);
2381 rank
= Rank
.getRankByEnglishName(value
, nc
, useUnknown
);
2383 if (rank
.equals(Rank
.UNKNOWN_RANK())) {
2386 } catch (UnknownCdmTypeException e
) {
2392 // public void handleNameNotRank(MarkupImportState state, XMLEventReader
2393 // reader, XMLEvent parentEvent, String classValue, NonViralName name)
2394 // throws XMLStreamException {
2395 // if (ACCEPTED.equalsIgnoreCase(classValue)){
2396 // }else if (SYNONYM.equalsIgnoreCase(classValue)){
2398 // //TODO Not yet implemented
2399 // handleNotYetImplementedAttributeValue(parentEvent, CLASS, classValue);
2403 private void handleCitation(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
, NonViralName name
) throws XMLStreamException
{
2404 String classValue
= getClassOnlyAttribute(parentEvent
);
2406 state
.setCitation(true);
2407 boolean hasRefPart
= false;
2408 Map
<String
, String
> refMap
= new HashMap
<String
, String
>();
2409 while (reader
.hasNext()) {
2410 XMLEvent next
= readNoWhitespace(reader
);
2411 if (isMyEndingElement(next
, parentEvent
)) {
2412 checkMandatoryElement(hasRefPart
, parentEvent
.asStartElement(),
2414 Reference
<?
> reference
= createReference(state
, refMap
, next
);
2415 String microReference
= refMap
.get(DETAILS
);
2416 doCitation(state
, name
, classValue
, reference
, microReference
,
2418 state
.setCitation(false);
2420 } else if (isStartingElement(next
, REF_PART
)) {
2421 handleRefPart(state
, reader
, next
, refMap
);
2424 handleUnexpectedElement(next
);
2427 throw new IllegalStateException("Citation has no closing tag");
2431 private void handleRefPart(MarkupImportState state
, XMLEventReader reader
,XMLEvent parentEvent
, Map
<String
, String
> refMap
) throws XMLStreamException
{
2432 String classValue
= getClassOnlyAttribute(parentEvent
);
2435 while (reader
.hasNext()) {
2436 XMLEvent next
= readNoWhitespace(reader
);
2437 if (isMyEndingElement(next
, parentEvent
)) {
2438 refMap
.put(classValue
, text
);
2440 } else if (next
.isStartElement()) {
2441 if (isStartingElement(next
, ANNOTATION
)) {
2442 handleNotYetImplementedElement(next
);
2443 } else if (isStartingElement(next
, ITALICS
)) {
2444 handleNotYetImplementedElement(next
);
2445 } else if (isStartingElement(next
, BOLD
)) {
2446 handleNotYetImplementedElement(next
);
2448 handleUnexpectedStartElement(next
.asStartElement());
2450 } else if (next
.isCharacters()) {
2451 text
+= next
.asCharacters().getData();
2453 handleUnexpectedEndElement(next
.asEndElement());
2456 throw new IllegalStateException("RefPart has no closing tag");
2460 private Reference
<?
> createReference(MarkupImportState state
, Map
<String
, String
> refMap
, XMLEvent parentEvent
) {
2462 Reference
<?
> reference
;
2464 String type
= getAndRemoveMapKey(refMap
, PUBTYPE
);
2465 String authorStr
= getAndRemoveMapKey(refMap
, AUTHOR
);
2466 String titleStr
= getAndRemoveMapKey(refMap
, PUBTITLE
);
2467 String titleCache
= getAndRemoveMapKey(refMap
, PUBFULLNAME
);
2468 String volume
= getAndRemoveMapKey(refMap
, VOLUME
);
2469 String edition
= getAndRemoveMapKey(refMap
, EDITION
);
2470 String editors
= getAndRemoveMapKey(refMap
, EDITORS
);
2471 String year
= getAndRemoveMapKey(refMap
, YEAR
);
2472 String pubName
= getAndRemoveMapKey(refMap
, PUBNAME
);
2473 String pages
= getAndRemoveMapKey(refMap
, PAGES
);
2475 if (state
.isCitation()) {
2476 if (volume
!= null || "journal".equalsIgnoreCase(type
)) {
2477 IArticle article
= ReferenceFactory
.newArticle();
2478 if (pubName
!= null) {
2479 IJournal journal
= ReferenceFactory
.newJournal();
2480 journal
.setTitle(pubName
);
2481 article
.setInJournal(journal
);
2483 reference
= (Reference
<?
>) article
;
2487 if (pubName
!= null){
2488 reference
= ReferenceFactory
.newBookSection();
2490 reference
= ReferenceFactory
.newBook();
2493 // TODO use existing author from name or before
2494 TeamOrPersonBase
<?
> author
= createAuthor(authorStr
);
2495 reference
.setAuthorTeam(author
);
2497 reference
.setTitle(titleStr
);
2498 if (StringUtils
.isNotBlank(titleCache
)) {
2499 reference
.setTitleCache(titleCache
, true);
2501 reference
.setEdition(edition
);
2502 reference
.setEditor(editors
);
2504 if (pubName
!= null) {
2505 Reference
<?
> inReference
;
2506 if (reference
.getType().equals(ReferenceType
.Article
)) {
2507 inReference
= ReferenceFactory
.newJournal();
2509 inReference
= ReferenceFactory
.newGeneric();
2511 inReference
.setTitle(pubName
);
2512 reference
.setInReference(inReference
);
2516 } else { //no citation
2517 if (volume
!= null || "journal".equalsIgnoreCase(type
)) {
2518 IArticle article
= ReferenceFactory
.newArticle();
2519 if (pubName
!= null) {
2520 IJournal journal
= ReferenceFactory
.newJournal();
2521 journal
.setTitle(pubName
);
2522 article
.setInJournal(journal
);
2524 reference
= (Reference
<?
>) article
;
2527 Reference
<?
> bookOrPartOf
= ReferenceFactory
.newGeneric();
2528 reference
= bookOrPartOf
;
2532 TeamOrPersonBase
<?
> author
= createAuthor(authorStr
);
2533 reference
.setAuthorTeam(author
);
2535 reference
.setTitle(titleStr
);
2536 if (StringUtils
.isNotBlank(titleCache
)) {
2537 reference
.setTitleCache(titleCache
, true);
2539 reference
.setEdition(edition
);
2540 reference
.setEditor(editors
);
2542 if (pubName
!= null) {
2543 Reference
<?
> inReference
;
2544 if (reference
.getType().equals(ReferenceType
.Article
)) {
2545 inReference
= ReferenceFactory
.newJournal();
2547 inReference
= ReferenceFactory
.newGeneric();
2549 inReference
.setTitle(pubName
);
2550 reference
.setInReference(inReference
);
2553 reference
.setVolume(volume
);
2554 reference
.setDatePublished(TimePeriod
.parseString(year
));
2555 //TODO check if this is handled correctly in FM markup
2556 reference
.setPages(pages
);
2559 String
[] unhandledList
= new String
[]{ALTERNATEPUBTITLE
, ISSUE
, NOTES
, STATUS
};
2560 for (String unhandled
: unhandledList
){
2561 String value
= getAndRemoveMapKey(refMap
, unhandled
);
2562 if (isNotBlank(value
)){
2563 this.handleNotYetImplementedAttributeValue(parentEvent
, CLASS
, unhandled
);
2567 for (String key
: refMap
.keySet()) {
2568 if (!DETAILS
.equalsIgnoreCase(key
)) {
2569 this.fireUnexpectedAttributeValue(parentEvent
, CLASS
, key
);
2576 private TeamOrPersonBase
createAuthor(String authorTitle
) {
2577 // TODO atomize and also use by name creation
2578 TeamOrPersonBase result
= Team
.NewTitledInstance(authorTitle
,
2583 private String
getAndRemoveMapKey(Map
<String
, String
> map
, String key
) {
2584 String result
= map
.get(key
);
2586 if (result
!= null) {
2587 result
= normalize(result
);
2589 return StringUtils
.stripToNull(result
);
2592 private void doCitation(MarkupImportState state
, NonViralName name
,
2593 String classValue
, Reference reference
, String microCitation
,
2594 XMLEvent parentEvent
) {
2595 if (PUBLICATION
.equalsIgnoreCase(classValue
)) {
2596 name
.setNomenclaturalReference(reference
);
2597 name
.setNomenclaturalMicroReference(microCitation
);
2598 } else if (USAGE
.equalsIgnoreCase(classValue
)) {
2599 Taxon taxon
= state
.getCurrentTaxon();
2600 TaxonDescription td
= getTaxonDescription(taxon
, state
2601 .getConfig().getSourceReference(), false, true);
2602 TextData citation
= TextData
.NewInstance(Feature
.CITATION());
2603 // TODO name used in source
2604 citation
.addSource(null, null, reference
, microCitation
);
2605 td
.addElement(citation
);
2606 } else if (TYPE
.equalsIgnoreCase(classValue
)) {
2607 handleNotYetImplementedAttributeValue(parentEvent
, CLASS
,
2610 // TODO Not yet implemented
2611 handleNotYetImplementedAttributeValue(parentEvent
, CLASS
,
2616 private void handleFeature(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
2617 String classValue
= getClassOnlyAttribute(parentEvent
);
2618 Feature feature
= makeFeature(classValue
, state
, parentEvent
);
2619 Taxon taxon
= state
.getCurrentTaxon();
2620 TaxonDescription taxonDescription
= getTaxonDescription(taxon
, state
.getConfig().getSourceReference(), NO_IMAGE_GALLERY
, CREATE_NEW
);
2621 // TextData figureHolderTextData = null; //for use with one TextData for
2624 boolean isDescription
= feature
.equals(Feature
.DESCRIPTION());
2625 DescriptionElementBase lastDescriptionElement
= null;
2627 while (reader
.hasNext()) {
2628 XMLEvent next
= readNoWhitespace(reader
);
2629 if (isMyEndingElement(next
, parentEvent
)) {
2631 } else if (isEndingElement(next
, DISTRIBUTION_LIST
) || isEndingElement(next
, HABITAT_LIST
)) {
2632 // only handle list elements
2633 } else if (isStartingElement(next
, HEADING
)) {
2634 makeFeatureHeading(state
, reader
, classValue
, feature
, next
);
2635 } else if (isStartingElement(next
, WRITER
)) {
2636 makeFeatureWriter(state
, reader
, feature
, taxon
, next
);
2637 // } else if (isStartingElement(next, DISTRIBUTION_LOCALITY)) {
2638 // if (!feature.equals(Feature.DISTRIBUTION())) {
2639 // String message = "Distribution locality only allowed for feature of type 'distribution'";
2640 // fireWarningEvent(message, next, 4);
2642 // handleDistributionLocality(state, reader, next);
2643 } else if (isStartingElement(next
, DISTRIBUTION_LIST
) || isStartingElement(next
, HABITAT_LIST
)) {
2644 // only handle single list elements
2645 } else if (isStartingElement(next
, HABITAT
)) {
2646 if (!(feature
.equals(Feature
.HABITAT())
2647 || feature
.equals(Feature
.HABITAT_ECOLOGY())
2648 || feature
.equals(Feature
.ECOLOGY()))) {
2649 String message
= "Habitat only allowed for feature of type 'habitat','habitat ecology' or 'ecology'";
2650 fireWarningEvent(message
, next
, 4);
2652 handleHabitat(state
, reader
, next
);
2653 } else if (isStartingElement(next
, CHAR
)) {
2654 TextData textData
= handleChar(state
, reader
, next
);
2655 taxonDescription
.addElement(textData
);
2656 } else if (isStartingElement(next
, STRING
)) {
2657 lastDescriptionElement
= makeFeatureString(state
, reader
,feature
, taxonDescription
, lastDescriptionElement
,next
);
2658 } else if (isStartingElement(next
, FIGURE_REF
)) {
2659 lastDescriptionElement
= makeFeatureFigureRef(state
, reader
, taxonDescription
, isDescription
, lastDescriptionElement
, next
);
2660 } else if (isStartingElement(next
, REFERENCES
)) {
2661 // TODO details/microcitation ??
2663 List
<Reference
<?
>> refs
= handleReferences(state
, reader
, next
);
2664 if (!refs
.isEmpty()) {
2666 Reference
<?
> descriptionRef
= state
.getConfig().getSourceReference();
2667 TaxonDescription description
= getTaxonDescription(taxon
, descriptionRef
, false, true);
2668 TextData featurePlaceholder
= docImport
.getFeaturePlaceholder(state
, description
, feature
, true);
2669 for (Reference
<?
> citation
: refs
) {
2670 featurePlaceholder
.addSource(null, null, citation
, null);
2673 String message
= "No reference found in references";
2674 fireWarningEvent(message
, next
, 6);
2676 } else if (isStartingElement(next
, NUM
)) {
2678 handleNotYetImplementedElement(next
);
2679 } else if (isEndingElement(next
, NUM
)) {
2681 popUnimplemented(next
.asEndElement());
2683 handleUnexpectedElement(next
);
2686 throw new IllegalStateException("<Feature> has no closing tag");
2692 * @param taxonDescription
2693 * @param isDescription
2694 * @param lastDescriptionElement
2697 * @throws XMLStreamException
2699 private DescriptionElementBase
makeFeatureFigureRef(MarkupImportState state
, XMLEventReader reader
,TaxonDescription taxonDescription
,
2700 boolean isDescription
, DescriptionElementBase lastDescriptionElement
, XMLEvent next
)throws XMLStreamException
{
2701 FigureDataHolder figureHolder
= handleFigureRef(state
, reader
, next
);
2702 Feature figureFeature
= getFeature(state
,MarkupTransformer
.uuidFigures
, "Figures", "Figures", "Fig.",null);
2703 if (isDescription
) {
2704 TextData figureHolderTextData
= null;
2705 // if (figureHolderTextData == null){
2706 figureHolderTextData
= TextData
.NewInstance(figureFeature
);
2707 if (StringUtils
.isNotBlank(figureHolder
.num
)) {
2708 String annotationText
= "<num>" + figureHolder
.num
.trim()
2710 Annotation annotation
= Annotation
.NewInstance(annotationText
,
2711 AnnotationType
.TECHNICAL(), Language
.DEFAULT());
2712 figureHolderTextData
.addAnnotation(annotation
);
2714 if (StringUtils
.isNotBlank(figureHolder
.figurePart
)) {
2715 String annotationText
= "<figurePart>"+ figureHolder
.figurePart
.trim() + "</figurePart>";
2716 Annotation annotation
= Annotation
.NewInstance(annotationText
,AnnotationType
.EDITORIAL(), Language
.DEFAULT());
2717 figureHolderTextData
.addAnnotation(annotation
);
2719 // if (StringUtils.isNotBlank(figureText)){
2720 // figureHolderTextData.putText(Language.DEFAULT(), figureText);
2722 taxonDescription
.addElement(figureHolderTextData
);
2724 registerFigureDemand(state
, figureHolderTextData
, figureHolder
.ref
);
2726 if (lastDescriptionElement
== null) {
2727 String message
= "No description element created yet that can be referred by figure. Create new TextData instead";
2728 fireWarningEvent(message
, next
, 4);
2729 lastDescriptionElement
= TextData
.NewInstance(figureFeature
);
2730 taxonDescription
.addElement(lastDescriptionElement
);
2732 registerFigureDemand(state
, lastDescriptionElement
,
2735 return lastDescriptionElement
;
2742 * @param taxonDescription
2743 * @param lastDescriptionElement
2744 * @param distributionList
2747 * @throws XMLStreamException
2749 private DescriptionElementBase
makeFeatureString(MarkupImportState state
,XMLEventReader reader
, Feature feature
,
2750 TaxonDescription taxonDescription
, DescriptionElementBase lastDescriptionElement
, XMLEvent next
) throws XMLStreamException
{
2752 if (feature
.equals(Feature
.SPECIMEN()) || feature
.equals(Feature
.MATERIALS_EXAMINED())){
2754 List
<DescriptionElementBase
> specimens
= handleMaterialsExamined(state
, reader
, next
);
2755 for (DescriptionElementBase specimen
: specimens
){
2756 taxonDescription
.addElement(specimen
);
2757 lastDescriptionElement
= specimen
;
2759 return lastDescriptionElement
;
2763 Map
<String
, String
> subheadingMap
= handleString(state
, reader
, next
, feature
);
2764 for (String subheading
: subheadingMap
.keySet()) {
2765 Feature subheadingFeature
= feature
;
2766 if (StringUtils
.isNotBlank(subheading
) && subheadingMap
.size() > 1) {
2767 subheadingFeature
= makeFeature(subheading
, state
, next
);
2769 if (feature
.equals(Feature
.COMMON_NAME())){
2770 List
<DescriptionElementBase
> commonNames
= makeVernacular(state
, subheading
, subheadingMap
.get(subheading
));
2771 for (DescriptionElementBase commonName
: commonNames
){
2772 taxonDescription
.addElement(commonName
);
2773 lastDescriptionElement
= commonName
;
2776 TextData textData
= TextData
.NewInstance(subheadingFeature
);
2777 textData
.putText(Language
.DEFAULT(), subheadingMap
.get(subheading
));
2778 taxonDescription
.addElement(textData
);
2779 lastDescriptionElement
= textData
;
2780 // TODO how to handle figures when these data are split in
2784 return lastDescriptionElement
;
2787 private List
<DescriptionElementBase
> makeVernacular(MarkupImportState state
, String subheading
, String commonNameString
) throws XMLStreamException
{
2788 List
<DescriptionElementBase
> result
= new ArrayList
<DescriptionElementBase
>();
2789 String
[] splits
= commonNameString
.split(",");
2790 for (String split
: splits
){
2791 split
= split
.trim();
2792 if (! split
.matches(".*\\(.*\\)\\.?")){
2793 fireWarningEvent("Common name string '"+split
+"' does not match given pattern", state
.getReader().peek(), 4);
2796 String name
= split
.replaceAll("\\(.*\\)", "").replace(".", "").trim();
2797 String languageStr
= split
.replaceFirst(".*\\(", "").replaceAll("\\)\\.?", "").trim();
2799 Language language
= null;
2800 if (StringUtils
.isNotBlank(languageStr
)){
2802 UUID langUuid
= state
.getTransformer().getLanguageUuid(languageStr
);
2803 TermVocabulary
<?
> voc
= null;
2804 language
= getLanguage(state
, langUuid
, languageStr
, languageStr
, null, voc
);
2805 if (language
== null){
2806 logger
.warn("Language " + languageStr
+ " not recognized by transformer");
2808 } catch (UndefinedTransformerMethodException e
) {
2809 throw new RuntimeException(e
);
2812 NamedArea area
= null;
2813 CommonTaxonName commonTaxonName
= CommonTaxonName
.NewInstance(name
, language
, area
);
2814 result
.add(commonTaxonName
);
2820 private List
<DescriptionElementBase
> handleMaterialsExamined(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
2821 List
<DescriptionElementBase
> result
= new ArrayList
<DescriptionElementBase
>();
2822 while (reader
.hasNext()) {
2823 XMLEvent next
= readNoWhitespace(reader
);
2824 if (isMyEndingElement(next
, parentEvent
)) {
2825 if (result
.isEmpty()){
2826 fireWarningEvent("Materials examined created empty Individual Associations list", parentEvent
, 4);
2829 } else if (isStartingElement(next
, SUB_HEADING
)) {
2830 handleNotYetImplementedElement(next
);
2831 } else if (isStartingElement(next
, BR
)) {
2832 handleNotYetImplementedElement(next
);
2833 } else if (isStartingElement(next
, GATHERING
)) {
2834 DerivedUnitFacade facade
= DerivedUnitFacade
.NewInstance(DerivedUnitType
.DerivedUnit
.DerivedUnit
);
2835 handleGathering(state
, reader
, next
, facade
);
2836 SpecimenOrObservationBase
<?
> specimen
;
2837 if (facade
.innerDerivedUnit() != null){
2838 specimen
= facade
.innerDerivedUnit();
2840 specimen
= facade
.innerFieldObservation();
2842 IndividualsAssociation individualsAssociation
= IndividualsAssociation
.NewInstance();
2843 individualsAssociation
.setAssociatedSpecimenOrObservation(specimen
);
2844 result
.add(individualsAssociation
);
2846 handleUnexpectedElement(next
);
2849 throw new IllegalStateException("<String> has no closing tag");
2859 * @throws XMLStreamException
2861 private void makeFeatureWriter(MarkupImportState state
,XMLEventReader reader
, Feature feature
, Taxon taxon
, XMLEvent next
) throws XMLStreamException
{
2862 WriterDataHolder writer
= handleWriter(state
, reader
, next
);
2863 if (isNotBlank(writer
.writer
)) {
2865 Reference
<?
> ref
= state
.getConfig().getSourceReference();
2866 TaxonDescription description
= getTaxonDescription(taxon
, ref
,
2868 TextData featurePlaceholder
= docImport
.getFeaturePlaceholder(state
,
2869 description
, feature
, true);
2870 featurePlaceholder
.addAnnotation(writer
.annotation
);
2871 registerFootnotes(state
, featurePlaceholder
, writer
.footnotes
);
2873 String message
= "Writer element is empty";
2874 fireWarningEvent(message
, next
, 4);
2884 * @throws XMLStreamException
2886 private void makeFeatureHeading(MarkupImportState state
, XMLEventReader reader
, String classValue
, Feature feature
, XMLEvent next
) throws XMLStreamException
{
2887 String heading
= handleHeading(state
, reader
, next
);
2888 if (StringUtils
.isNotBlank(heading
)) {
2889 if (!heading
.equalsIgnoreCase(classValue
)) {
2891 if (!feature
.equals(state
.getTransformer().getFeatureByKey(
2893 UUID headerFeatureUuid
= state
.getTransformer()
2894 .getFeatureUuid(heading
);
2895 if (!feature
.getUuid().equals(headerFeatureUuid
)) {
2896 String message
= "Feature heading '%s' differs from feature class '%s' and can not be transformed to feature";
2897 message
= String
.format(message
, heading
,
2899 fireWarningEvent(message
, next
, 1);
2902 } catch (UndefinedTransformerMethodException e
) {
2903 throw new RuntimeException(e
);
2911 private List
<Reference
<?
>> handleReferences(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
2913 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
2914 String bibliography
= getAndRemoveAttributeValue(attributes
,
2916 String serialsAbbreviations
= getAndRemoveAttributeValue(attributes
,
2917 SERIALS_ABBREVIATIONS
);
2918 if (isNotBlank(bibliography
) || isNotBlank(serialsAbbreviations
)) {
2919 String message
= "Attributes not yet implemented for <references>";
2920 fireWarningEvent(message
, parentEvent
, 4);
2923 List
<Reference
<?
>> result
= new ArrayList
<Reference
<?
>>();
2926 while (reader
.hasNext()) {
2927 XMLEvent next
= readNoWhitespace(reader
);
2928 if (next
.isEndElement()) {
2929 if (isMyEndingElement(next
, parentEvent
)) {
2932 if (isEndingElement(next
, HEADING
)) {
2933 // NOT YET IMPLEMENTED
2934 popUnimplemented(next
.asEndElement());
2935 } else if (isEndingElement(next
, WRITER
)) {
2936 // NOT YET IMPLEMENTED
2937 popUnimplemented(next
.asEndElement());
2938 } else if (isEndingElement(next
, FOOTNOTE
)) {
2939 // NOT YET IMPLEMENTED
2940 popUnimplemented(next
.asEndElement());
2941 } else if (isEndingElement(next
, STRING
)) {
2942 // NOT YET IMPLEMENTED
2943 popUnimplemented(next
.asEndElement());
2944 } else if (isEndingElement(next
, REF_NUM
)) {
2945 // NOT YET IMPLEMENTED
2946 popUnimplemented(next
.asEndElement());
2948 handleUnexpectedEndElement(next
.asEndElement());
2951 } else if (next
.isStartElement()) {
2952 if (isStartingElement(next
, HEADING
)) {
2953 handleNotYetImplementedElement(next
);
2954 } else if (isStartingElement(next
, SUB_HEADING
)) {
2955 String subheading
= getCData(state
, reader
, next
).trim();
2956 String excludePattern
= "(i?)(References?|Literature):?";
2957 if (!subheading
.matches(excludePattern
)) {
2958 fireNotYetImplementedElement(next
.getLocation(), next
.asStartElement().getName(), 0);
2960 } else if (isStartingElement(next
, WRITER
)) {
2961 handleNotYetImplementedElement(next
);
2962 } else if (isStartingElement(next
, FOOTNOTE
)) {
2963 handleNotYetImplementedElement(next
);
2964 } else if (isStartingElement(next
, STRING
)) {
2965 handleNotYetImplementedElement(next
);
2966 } else if (isStartingElement(next
, REF_NUM
)) {
2967 handleNotYetImplementedElement(next
);
2968 } else if (isStartingElement(next
, REFERENCE
)) {
2969 Reference
<?
> ref
= handleReference(state
, reader
, next
);
2972 handleUnexpectedStartElement(next
);
2975 handleUnexpectedElement(next
);
2978 throw new IllegalStateException("<References> has no closing tag");
2981 private void handleHabitat(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
2982 checkNoAttributes(parentEvent
);
2983 Taxon taxon
= state
.getCurrentTaxon();
2984 // TODO which ref to take?
2985 Reference
<?
> ref
= state
.getConfig().getSourceReference();
2988 while (reader
.hasNext()) {
2989 XMLEvent next
= readNoWhitespace(reader
);
2990 if (isMyEndingElement(next
, parentEvent
)) {
2991 TaxonDescription description
= getTaxonDescription(taxon
, ref
,
2993 UUID uuidExtractedHabitat
= MarkupTransformer
.uuidExtractedHabitat
;
2994 Feature feature
= getFeature(
2996 uuidExtractedHabitat
,
2997 "Extracted Habitat",
2998 "An structured habitat that was extracted from a habitat text",
2999 "extr. habit.", null);
3000 TextData habitat
= TextData
.NewInstance(feature
);
3001 habitat
.putText(Language
.DEFAULT(), text
);
3002 description
.addElement(habitat
);
3005 } else if (next
.isStartElement()) {
3006 if (isStartingElement(next
, ALTITUDE
)) {
3007 text
= text
.trim() + getTaggedCData(state
, reader
, next
);
3008 } else if (isStartingElement(next
, LIFE_CYCLE_PERIODS
)) {
3009 handleNotYetImplementedElement(next
);
3011 handleUnexpectedStartElement(next
.asStartElement());
3013 } else if (next
.isCharacters()) {
3014 text
+= next
.asCharacters().getData();
3016 handleUnexpectedElement(next
);
3019 throw new IllegalStateException("<Habitat> has no closing tag");
3022 private String
getTaggedCData(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
) throws XMLStreamException
{
3023 checkNoAttributes(parentEvent
);
3025 String text
= getXmlTag(parentEvent
);
3026 while (reader
.hasNext()) {
3027 XMLEvent next
= readNoWhitespace(reader
);
3028 if (isMyEndingElement(next
, parentEvent
)) {
3029 text
+= getXmlTag(next
);
3031 } else if (next
.isStartElement()) {
3032 text
+= getTaggedCData(state
, reader
, next
);
3033 } else if (next
.isEndElement()) {
3034 text
+= getTaggedCData(state
, reader
, next
);
3035 } else if (next
.isCharacters()) {
3036 text
+= next
.asCharacters().getData();
3038 handleUnexpectedEndElement(next
.asEndElement());
3041 throw new IllegalStateException("Some tag has no closing tag");
3044 private String
handleDistributionLocality(MarkupImportState state
,XMLEventReader reader
, XMLEvent parentEvent
)throws XMLStreamException
{
3045 Map
<String
, Attribute
> attributes
= getAttributes(parentEvent
);
3046 String classValue
= getAndRemoveRequiredAttributeValue(parentEvent
, attributes
, CLASS
);
3047 String statusValue
=getAndRemoveAttributeValue(attributes
, STATUS
);
3048 String frequencyValue
=getAndRemoveAttributeValue(attributes
, FREQUENCY
);
3051 Taxon taxon
= state
.getCurrentTaxon();
3052 // TODO which ref to take?
3053 Reference
<?
> ref
= state
.getConfig().getSourceReference();
3056 while (reader
.hasNext()) {
3057 XMLEvent next
= readNoWhitespace(reader
);
3058 if (isMyEndingElement(next
, parentEvent
)) {
3059 if (StringUtils
.isNotBlank(text
)) {
3060 String label
= CdmUtils
.removeTrailingDot(normalize(text
));
3061 TaxonDescription description
= getTaxonDescription(taxon
, ref
, false, true);
3062 NamedAreaLevel level
= makeNamedAreaLevel(state
,classValue
, next
);
3065 PresenceAbsenceTermBase
<?
> status
= null;
3066 if (isNotBlank(statusValue
)){
3068 status
= state
.getTransformer().getPresenceTermByKey(statusValue
);
3069 if (status
== null){
3071 String message
= "The status '%s' could not be transformed to an CDM status";
3072 fireWarningEvent(message
, next
, 4);
3074 } catch (UndefinedTransformerMethodException e
) {
3075 throw new RuntimeException(e
);
3078 status
= PresenceTerm
.PRESENT();
3081 if (isNotBlank(frequencyValue
)){
3082 String message
= "The frequency attribute is currently not yet available in CDM";
3083 fireWarningEvent(message
, parentEvent
, 6);
3086 NamedArea higherArea
= null;
3087 List
<NamedArea
> areas
= new ArrayList
<NamedArea
>();
3089 String patSingleArea
= "([^,\\(]{3,})";
3090 String patSeparator
= "(,|\\sand\\s)";
3091 String hierarchiePattern
= String
.format("%s\\((%s(%s%s)*)\\)",patSingleArea
, patSingleArea
, patSeparator
, patSingleArea
);
3092 Pattern patHierarchie
= Pattern
.compile(hierarchiePattern
, Pattern
.CASE_INSENSITIVE
);
3093 Matcher matcher
= patHierarchie
.matcher(label
);
3094 if (matcher
.matches()){
3095 String higherAreaStr
= matcher
.group(1).trim();
3096 higherArea
= makeArea(state
, higherAreaStr
, level
);
3097 String
[] innerAreas
= matcher
.group(2).split(patSeparator
);
3098 for (String innerArea
: innerAreas
){
3099 if (isNotBlank(innerArea
)){
3100 NamedArea singleArea
= makeArea(state
, innerArea
.trim(), level
);
3101 areas
.add(singleArea
);
3102 NamedArea partOf
= singleArea
.getPartOf();
3103 // if (partOf == null){
3104 // singleArea.setPartOf(higherArea);
3109 NamedArea singleArea
= makeArea(state
, label
, level
);
3110 areas
.add(singleArea
);
3113 for (NamedArea area
: areas
){
3114 //create distribution
3115 Distribution distribution
= Distribution
.NewInstance(area
,status
);
3116 description
.addElement(distribution
);
3119 String message
= "Empty distribution locality";
3120 fireWarningEvent(message
, next
, 4);
3123 } else if (isStartingElement(next
, COORDINATES
)) {
3125 handleNotYetImplementedElement(next
);
3126 } else if (isEndingElement(next
, COORDINATES
)) {
3128 popUnimplemented(next
.asEndElement());
3129 } else if (next
.isCharacters()) {
3130 text
+= next
.asCharacters().getData();
3132 handleUnexpectedEndElement(next
.asEndElement());
3135 throw new IllegalStateException("<DistributionLocality> has no closing tag");
3144 private NamedArea
makeArea(MarkupImportState state
, String areaName
, NamedAreaLevel level
) {
3147 //TODO FM vocabulary
3148 TermVocabulary
<NamedArea
> voc
= null;
3149 NamedAreaType areaType
= null;
3151 NamedArea area
= null;
3153 area
= state
.getTransformer().getNamedAreaByKey(areaName
);
3154 } catch (UndefinedTransformerMethodException e
) {
3155 throw new RuntimeException(e
);
3158 boolean isNewInState
= false;
3159 UUID uuid
= state
.getAreaUuid(areaName
);
3161 isNewInState
= true;
3165 uuid
= state
.getTransformer().getNamedAreaUuid(areaName
);
3166 } catch (UndefinedTransformerMethodException e
) {
3167 throw new RuntimeException(e
);
3171 CdmImportBase
.TermMatchMode matchMode
= CdmImportBase
.TermMatchMode
.UUID_LABEL
;
3172 area
= getNamedArea(state
, uuid
, areaName
, areaName
, areaName
, areaType
, level
, voc
, matchMode
);
3174 state
.putAreaUuid(areaName
, area
.getUuid());
3176 //TODO just for testing -> make generic and move to better place
3177 String geoServiceLayer
="vmap0_as_bnd_political_boundary_a";
3178 String layerFieldName
="nam";
3180 if ("Bangka".equals(areaName
)){
3181 String areaValue
= "PULAU BANGKA#SUMATERA SELATAN";
3182 GeoServiceArea geoServiceArea
= new GeoServiceArea();
3183 geoServiceArea
.add(geoServiceLayer
, layerFieldName
, areaValue
);
3184 this.editGeoService
.setMapping(area
, geoServiceArea
);
3185 // save(area, state);
3187 if ("Luzon".equals(areaName
)){
3188 GeoServiceArea geoServiceArea
= new GeoServiceArea();
3190 List
<String
> list
= Arrays
.asList("HERMANA MAYOR ISLAND#CENTRAL LUZON",
3191 "HERMANA MENOR ISLAND#CENTRAL LUZON",
3193 for (String areaValue
: list
){
3194 geoServiceArea
.add(geoServiceLayer
, layerFieldName
, areaValue
);
3197 this.editGeoService
.setMapping(area
, geoServiceArea
);
3198 // save(area, state);
3200 if ("Mindanao".equals(areaName
)){
3201 GeoServiceArea geoServiceArea
= new GeoServiceArea();
3203 List
<String
> list
= Arrays
.asList("NORTHERN MINDANAO",
3204 "SOUTHERN MINDANAO",
3205 "WESTERN MINDANAO");
3206 //TODO to be continued
3207 for (String areaValue
: list
){
3208 geoServiceArea
.add(geoServiceLayer
, layerFieldName
, areaValue
);
3211 this.editGeoService
.setMapping(area
, geoServiceArea
);
3212 // save(area, state);
3214 if ("Palawan".equals(areaName
)){
3215 GeoServiceArea geoServiceArea
= new GeoServiceArea();
3217 List
<String
> list
= Arrays
.asList("PALAWAN#SOUTHERN TAGALOG");
3218 for (String areaValue
: list
){
3219 geoServiceArea
.add(geoServiceLayer
, layerFieldName
, areaValue
);
3222 this.editGeoService
.setMapping(area
, geoServiceArea
);
3223 // save(area, state);
3235 * @param levelString
3239 private NamedAreaLevel
makeNamedAreaLevel(MarkupImportState state
,
3240 String levelString
, XMLEvent next
) {
3241 NamedAreaLevel level
;
3243 level
= state
.getTransformer().getNamedAreaLevelByKey(levelString
);
3244 if (level
== null) {
3245 UUID levelUuid
= state
.getTransformer().getNamedAreaLevelUuid(levelString
);
3246 if (levelUuid
== null) {
3247 String message
= "Unknown distribution locality class (named area level): %s. Create new level instead.";
3248 message
= String
.format(message
, levelString
);
3249 fireWarningEvent(message
, next
, 6);
3251 level
= getNamedAreaLevel(state
, levelUuid
, levelString
, levelString
, levelString
, null);
3253 } catch (UndefinedTransformerMethodException e
) {
3254 throw new RuntimeException(e
);
3259 private String
handleHeading(MarkupImportState state
,XMLEventReader reader
, XMLEvent parentEvent
)throws XMLStreamException
{
3260 checkNoAttributes(parentEvent
);
3263 while (reader
.hasNext()) {
3264 XMLEvent next
= readNoWhitespace(reader
);
3265 if (isMyEndingElement(next
, parentEvent
)) {
3267 } else if (next
.isStartElement()) {
3268 if (isStartingElement(next
, FOOTNOTE
)) {
3269 handleNotYetImplementedElement(next
);
3271 handleUnexpectedStartElement(next
.asStartElement());
3273 } else if (next
.isCharacters()) {
3274 text
+= next
.asCharacters().getData();
3276 handleUnexpectedEndElement(next
.asEndElement());
3279 throw new IllegalStateException("<String> has no closing tag");
3287 * @param parentEvent
3288 * @param feature only needed for distributionLocalities
3290 * @throws XMLStreamException
3292 private Map
<String
, String
> handleString(MarkupImportState state
, XMLEventReader reader
, XMLEvent parentEvent
, Feature feature
)throws XMLStreamException
{
3294 String classValue
= getClassOnlyAttribute(parentEvent
, false);
3295 if (StringUtils
.isNotBlank(classValue
)) {
3296 String message
= "class attribute for <string> not yet implemented";
3297 fireWarningEvent(message
, parentEvent
, 2);
3301 Map
<String
, String
> subHeadingMap
= new HashMap
<String
, String
>();
3302 String currentSubheading
= null;
3304 boolean isTextMode
= true;
3306 while (reader
.hasNext()) {
3307 XMLEvent next
= readNoWhitespace(reader
);
3308 if (isMyEndingElement(next
, parentEvent
)) {
3309 putCurrentSubheading(subHeadingMap
, currentSubheading
, text
);
3310 return subHeadingMap
;
3311 } else if (isStartingElement(next
, BR
)) {
3314 } else if (isEndingElement(next
, BR
)) {
3316 } else if (isHtml(next
)) {
3317 text
+= getXmlTag(next
);
3318 } else if (isStartingElement(next
, SUB_HEADING
)) {
3319 text
= putCurrentSubheading(subHeadingMap
,currentSubheading
, text
);
3321 currentSubheading
= getCData(state
, reader
, next
).trim();
3322 } else if (isStartingElement(next
, DISTRIBUTION_LOCALITY
)) {
3323 if (feature
!= null && !feature
.equals(Feature
.DISTRIBUTION())) {
3324 String message
= "Distribution locality only allowed for feature of type 'distribution'";
3325 fireWarningEvent(message
, next
, 4);
3327 text
+= handleDistributionLocality(state
, reader
, next
);
3328 } else if (next
.isCharacters()) {
3330 String message
= "String is not in text mode";
3331 fireWarningEvent(message
, next
, 6);
3333 text
+= next
.asCharacters().getData();
3335 } else if (isStartingElement(next
, HEADING
)) {
3337 handleNotYetImplementedElement(next
);
3338 } else if (isEndingElement(next
, HEADING
)) {
3340 popUnimplemented(next
.asEndElement());
3341 } else if (isStartingElement(next
, QUOTE
)) {
3343 handleNotYetImplementedElement(next
);
3344 } else if (isEndingElement(next
, QUOTE
)) {
3346 popUnimplemented(next
.asEndElement());
3347 } else if (isStartingElement(next
, DEDICATION
)) {
3349 handleNotYetImplementedElement(next
);
3350 } else if (isEndingElement(next
, DEDICATION
)) {
3352 popUnimplemented(next
.asEndElement());
3353 } else if (isStartingElement(next
, TAXONTYPE
)) {
3355 handleNotYetImplementedElement(next
);
3356 } else if (isEndingElement(next
, TAXONTYPE
)) {
3358 popUnimplemented(next
.asEndElement());
3359 } else if (isStartingElement(next
, FULL_NAME
)) {
3361 handleNotYetImplementedElement(next
);
3362 } else if (isEndingElement(next
, FULL_NAME
)) {
3364 popUnimplemented(next
.asEndElement());
3365 }else if (isStartingElement(next
, REFERENCES
)) {
3367 handleNotYetImplementedElement(next
);
3368 } else if (isEndingElement(next
, REFERENCES
)) {
3370 popUnimplemented(next
.asEndElement());
3371 } else if (isStartingElement(next
, GATHERING
)) {
3373 handleNotYetImplementedElement(next
);
3374 } else if (isEndingElement(next
, GATHERING
)) {
3376 popUnimplemented(next
.asEndElement());
3377 } else if (isStartingElement(next
, ANNOTATION
)) {
3379 handleNotYetImplementedElement(next
);
3380 } else if (isEndingElement(next
, ANNOTATION
)) {
3382 popUnimplemented(next
.asEndElement());
3383 } else if (isStartingElement(next
, HABITAT
)) {
3385 handleNotYetImplementedElement(next
);
3386 } else if (isEndingElement(next
, HABITAT
)) {
3388 popUnimplemented(next
.asEndElement());
3389 } else if (isStartingElement(next
, FIGURE_REF
)) {
3391 handleNotYetImplementedElement(next
);
3392 } else if (isEndingElement(next
, FIGURE_REF
)) {
3394 popUnimplemented(next
.asEndElement());
3395 } else if (isStartingElement(next
, FIGURE
)) {
3397 handleNotYetImplementedElement(next
);
3398 } else if (isEndingElement(next
, FIGURE
)) {
3400 popUnimplemented(next
.asEndElement());
3401 } else if (isStartingElement(next
, FOOTNOTE_REF
)) {
3403 handleNotYetImplementedElement(next
);
3404 } else if (isEndingElement(next
, FOOTNOTE_REF
)) {
3406 popUnimplemented(next
.asEndElement());
3407 } else if (isStartingElement(next
, FOOTNOTE
)) {
3409 handleNotYetImplementedElement(next
);
3410 } else if (isEndingElement(next
, FOOTNOTE
)) {
3412 popUnimplemented(next
.asEndElement());
3413 } else if (isStartingElement(next
, WRITER
)) {
3415 handleNotYetImplementedElement(next
);
3416 } else if (isEndingElement(next
, WRITER
)) {
3418 popUnimplemented(next
.asEndElement());
3419 } else if (isStartingElement(next
, DATES
)) {
3421 handleNotYetImplementedElement(next
);
3422 } else if (isEndingElement(next
, DATES
)) {
3424 popUnimplemented(next
.asEndElement());
3426 handleUnexpectedElement(next
);
3429 throw new IllegalStateException("<String> has no closing tag");
3433 * @param subHeadingMap
3434 * @param currentSubheading
3438 private String
putCurrentSubheading(Map
<String
, String
> subHeadingMap
, String currentSubheading
, String text
) {
3439 if (StringUtils
.isNotBlank(text
)) {
3440 text
= removeStartingMinus(text
);
3441 subHeadingMap
.put(currentSubheading
, text
.trim());
3446 private String
removeStartingMinus(String string
) {
3447 string
= replaceStart(string
, "-");
3448 string
= replaceStart(string
, "\u002d");
3449 string
= replaceStart(string
, "\u2013");
3450 string
= replaceStart(string
, "\u2014");
3451 string
= replaceStart(string
, "--");
3457 * @param replacementString
3459 private String
replaceStart(String value
, String replacementString
) {
3460 if (value
.startsWith(replacementString
) ){
3461 value
= value
.substring(replacementString
.length()).trim();
3463 while (value
.startsWith("-") || value
.startsWith("\u2014") ){
3464 value
= value
.substring("-".length()).trim();
3469 private String
getXmlTag(XMLEvent event
) {
3471 if (event
.isStartElement()) {
3472 result
= "<" + event
.asStartElement().getName().getLocalPart()
3474 } else if (event
.isEndElement()) {
3475 result
= "</" + event
.asEndElement().getName().getLocalPart() + ">";
3477 String message
= "Only start or end elements are allowed as Html tags";
3478 throw new IllegalStateException(message
);
3483 protected static final List
<String
> htmlList
= Arrays
.asList("sub", "sup",
3484 "ol", "ul", "li", "i", "b", "table", "br","tr","td");
3486 private boolean isHtml(XMLEvent event
) {
3487 if (event
.isStartElement()) {
3488 String tag
= event
.asStartElement().getName().getLocalPart();
3489 return htmlList
.contains(tag
);
3490 } else if (event
.isEndElement()) {
3491 String tag
= event
.asEndElement().getName().getLocalPart();
3492 return htmlList
.contains(tag
);
3499 private TextData
handleChar(MarkupImportState state
, XMLEventReader reader
,
3500 XMLEvent parentEvent
) throws XMLStreamException
{
3501 String classValue
= getClassOnlyAttribute(parentEvent
);
3502 Feature feature
= makeFeature(classValue
, state
, parentEvent
);
3504 boolean isTextMode
= true;
3506 while (reader
.hasNext()) {
3507 XMLEvent next
= readNoWhitespace(reader
);
3508 if (isMyEndingElement(next
, parentEvent
)) {
3509 TextData textData
= TextData
.NewInstance(feature
);
3510 textData
.putText(Language
.DEFAULT(), text
);
3512 } else if (isStartingElement(next
, FIGURE_REF
)) {
3514 handleNotYetImplementedElement(next
);
3515 } else if (isEndingElement(next
, FIGURE_REF
)) {
3517 popUnimplemented(next
.asEndElement());
3518 } else if (isStartingElement(next
, FOOTNOTE_REF
)) {
3520 handleNotYetImplementedElement(next
);
3521 } else if (isEndingElement(next
, FOOTNOTE_REF
)) {
3523 popUnimplemented(next
.asEndElement());
3524 } else if (isStartingElement(next
, BR
)) {
3527 } else if (isEndingElement(next
, BR
)) {
3529 } else if (isHtml(next
)) {
3530 text
+= getXmlTag(next
);
3531 } else if (next
.isStartElement()) {
3532 if (isStartingElement(next
, ANNOTATION
)) {
3533 handleNotYetImplementedElement(next
);
3534 } else if (isStartingElement(next
, ITALICS
)) {
3535 handleNotYetImplementedElement(next
);
3536 } else if (isStartingElement(next
, BOLD
)) {
3537 handleNotYetImplementedElement(next
);
3538 } else if (isStartingElement(next
, FIGURE
)) {
3539 handleFigure(state
, reader
, next
);
3540 } else if (isStartingElement(next
, FOOTNOTE
)) {
3541 FootnoteDataHolder footnote
= handleFootnote(state
, reader
, next
);
3542 if (footnote
.isRef()) {
3543 String message
= "Ref footnote not implemented here";
3544 fireWarningEvent(message
, next
, 4);
3546 registerGivenFootnote(state
, footnote
);
3549 handleUnexpectedStartElement(next
.asStartElement());
3551 } else if (next
.isCharacters()) {
3553 String message
= "String is not in text mode";
3554 fireWarningEvent(message
, next
, 6);
3556 text
+= next
.asCharacters().getData();
3559 handleUnexpectedEndElement(next
.asEndElement());
3562 throw new IllegalStateException("RefPart has no closing tag");
3568 * @param parentEvent
3570 * @throws UndefinedTransformerMethodException
3572 private Feature
makeFeature(String classValue
, MarkupImportState state
, XMLEvent parentEvent
) {
3575 Feature feature
= state
.getTransformer().getFeatureByKey(classValue
);
3576 if (feature
!= null) {
3579 uuid
= state
.getTransformer().getFeatureUuid(classValue
);
3582 String message
= "Uuid is not defined for '%s'";
3583 message
= String
.format(message
, classValue
);
3584 fireWarningEvent(message
, parentEvent
, 8);
3586 String featureText
= StringUtils
.capitalize(classValue
);
3588 // TODO eFlora vocabulary
3589 TermVocabulary
<Feature
> voc
= null;
3590 feature
= getFeature(state
, uuid
, featureText
, featureText
, classValue
, voc
);
3591 if (feature
== null) {
3592 throw new NullPointerException(classValue
+ " not recognized as a feature");
3595 } catch (Exception e
) {
3596 String message
= "Could not create feature for %s: %s";
3597 message
= String
.format(message
, classValue
, e
.getMessage());
3598 fireWarningEvent(message
, parentEvent
, 4);
3599 return Feature
.UNKNOWN();