rename Reference.authorTeam -> authorship #4432
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / markup / MarkupDocumentImportNoComponent.java
1 /**
2 * Copyright (C) 2009 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9
10 package eu.etaxonomy.cdm.io.markup;
11
12 import java.net.MalformedURLException;
13 import java.net.URL;
14 import java.util.HashSet;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.UUID;
18
19 import javax.xml.stream.Location;
20 import javax.xml.stream.XMLEventReader;
21 import javax.xml.stream.XMLStreamException;
22 import javax.xml.stream.events.Attribute;
23 import javax.xml.stream.events.StartElement;
24 import javax.xml.stream.events.XMLEvent;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.log4j.Logger;
28
29 import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
30 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
31 import eu.etaxonomy.cdm.model.common.ExtensionType;
32 import eu.etaxonomy.cdm.model.common.Language;
33 import eu.etaxonomy.cdm.model.common.OriginalSourceType;
34 import eu.etaxonomy.cdm.model.common.TermVocabulary;
35 import eu.etaxonomy.cdm.model.description.CommonTaxonName;
36 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
37 import eu.etaxonomy.cdm.model.description.Distribution;
38 import eu.etaxonomy.cdm.model.description.Feature;
39 import eu.etaxonomy.cdm.model.description.PolytomousKey;
40 import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
41 import eu.etaxonomy.cdm.model.description.TaxonDescription;
42 import eu.etaxonomy.cdm.model.description.TextData;
43 import eu.etaxonomy.cdm.model.name.CultivarPlantName;
44 import eu.etaxonomy.cdm.model.name.NonViralName;
45 import eu.etaxonomy.cdm.model.name.Rank;
46 import eu.etaxonomy.cdm.model.reference.Reference;
47 import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
48 import eu.etaxonomy.cdm.model.taxon.Classification;
49 import eu.etaxonomy.cdm.model.taxon.Taxon;
50 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
51
52
53 /**
54 * @author a.mueller
55 *
56 */
57 public class MarkupDocumentImportNoComponent extends MarkupImportBase {
58 @SuppressWarnings("unused")
59 private static final Logger logger = Logger.getLogger(MarkupDocumentImportNoComponent.class);
60
61 private MarkupKeyImport keyImport;
62
63 private MarkupModsImport modsImport;
64 private MarkupFeatureImport featureImport;
65 private MarkupSpecimenImport specimenImport;
66 private MarkupNomenclatureImport nomenclatureImport;
67
68 public MarkupDocumentImportNoComponent(MarkupDocumentImport docImport) {
69 super(docImport);
70 this.keyImport = new MarkupKeyImport(docImport);
71 this.specimenImport = new MarkupSpecimenImport(docImport);
72 this.nomenclatureImport = new MarkupNomenclatureImport(docImport, specimenImport);
73 this.modsImport = new MarkupModsImport(docImport);
74 this.featureImport = new MarkupFeatureImport(docImport, specimenImport, nomenclatureImport);
75 }
76
77 public void doInvoke(MarkupImportState state) throws XMLStreamException {
78 XMLEventReader reader = state.getReader();
79
80 // publication (= root element)
81 String elName = PUBLICATION;
82 boolean hasPublication = false;
83
84 while (reader.hasNext()) {
85 XMLEvent nextEvent = reader.nextEvent();
86 if (isStartingElement(nextEvent, elName)) {
87 handlePublication(state, reader, nextEvent, elName);
88 hasPublication = true;
89 } else if (nextEvent.isEndDocument()) {
90 if (!hasPublication) {
91 String message = "No publication root element found";
92 fireWarningEvent(message, nextEvent, 8);
93 }
94 // done
95 } else {
96 fireSchemaConflictEventExpectedStartTag(elName, reader);
97 }
98 }
99
100
101 return;
102
103 }
104
105 private void handlePublication(MarkupImportState state, XMLEventReader reader, XMLEvent currentEvent, String elName) throws XMLStreamException {
106
107 // attributes
108 StartElement element = currentEvent.asStartElement();
109 Map<String, Attribute> attributes = getAttributes(element);
110 String lang = getAndRemoveAttributeValue(attributes, "lang");
111 if (lang != null){
112 Language language = getTermService().getLanguageByIso(lang);
113 state.setDefaultLanguage(language);
114 }
115
116 handleUnexpectedAttributes(element.getLocation(), attributes, "noNamespaceSchemaLocation");
117
118 while (reader.hasNext()) {
119 XMLEvent event = readNoWhitespace(reader);
120 // TODO cardinality of alternative
121 if (event.isEndElement()) {
122 if (isEndingElement(event, elName)) {
123 return;
124 } else {
125 if (isEndingElement(event, BIOGRAPHIES)) {
126 // NOT YET IMPLEMENTED
127 popUnimplemented(event.asEndElement());
128 } else if (isEndingElement(event, REFERENCES)) {
129 // NOT YET IMPLEMENTED
130 popUnimplemented(event.asEndElement());
131 } else if (isEndingElement(event, TEXT_SECTION)) {
132 // NOT YET IMPLEMENTED
133 popUnimplemented(event.asEndElement());
134 } else if (isEndingElement(event, ADDENDA)) {
135 // NOT YET IMPLEMENTED
136 popUnimplemented(event.asEndElement());
137 } else {
138 handleUnexpectedElement(event);
139 }
140 }
141 } else if (event.isStartElement()) {
142 if (isStartingElement(event, META_DATA)) {
143 handleMetaData(state, reader, event);
144 } else if (isStartingElement(event, TREATMENT)) {
145 handleTreatment(state, reader, event);
146 } else if (isStartingElement(event, BIOGRAPHIES)) {
147 handleNotYetImplementedElement(event);
148 } else if (isStartingElement(event, REFERENCES)) {
149 handleNotYetImplementedElement(event);
150 } else if (isStartingElement(event, TEXT_SECTION)) {
151 handleNotYetImplementedElement(event);
152 } else if (isStartingElement(event, ADDENDA)) {
153 handleNotYetImplementedElement(event);
154 } else {
155 handleUnexpectedStartElement(event);
156 }
157 } else {
158 handleUnexpectedElement(event);
159 }
160 }
161 throw new IllegalStateException("Publication has no ending element");
162 }
163
164 private void handleMetaData(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
165 checkNoAttributes(parentEvent);
166
167 while (reader.hasNext()) {
168 XMLEvent next = readNoWhitespace(reader);
169 if (isMyEndingElement(next, parentEvent)) {
170 return;
171 } else if (isStartingElement(next, DEFAULT_MEDIA_URL)) {
172 String baseUrl = getCData(state, reader, next);
173 try {
174 new URL(baseUrl);
175 state.setBaseMediaUrl(baseUrl);
176 } catch (MalformedURLException e) {
177 String message = "defaultMediaUrl '%s' is not a valid URL";
178 message = String.format(message, baseUrl);
179 fireWarningEvent(message, next, 8);
180 }
181 } else if (isStartingElement(next, MODS)){
182 modsImport.handleMods(state, reader, next);
183 } else {
184 handleUnexpectedElement(next);
185 }
186 }
187 throw new IllegalStateException("MetaData has no ending element");
188
189 }
190
191 private void handleTreatment(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
192 checkNoAttributes(parentEvent);
193 Taxon lastTaxon = null;
194 while (reader.hasNext()) {
195 XMLEvent next = readNoWhitespace(reader);
196 if (isMyEndingElement(next, parentEvent)) {
197 Set<PolytomousKeyNode> keyNodesToSave = state.getPolytomousKeyNodesToSave();
198 //better save the key then the nodes
199 Set<PolytomousKey> keySet = new HashSet<PolytomousKey>();
200 for (PolytomousKeyNode node : keyNodesToSave){
201 PolytomousKey key = node.getKey();
202 keySet.add(key);
203 }
204 save(keySet, state);
205 //unmatched key leads
206 UnmatchedLeads unmatched = state.getUnmatchedLeads();
207 if (unmatched.size() > 0){
208 String message = "The following key leads are unmatched: %s";
209 message = String.format(message, state.getUnmatchedLeads().toString());
210 fireWarningEvent(message, next, 6);
211 }
212 // save(keyNodesToSave, state);
213
214 return;
215 } else if (isStartingElement(next, TAXON)) {
216 Taxon thisTaxon = handleTaxon(state, reader, next.asStartElement());
217 doTaxonRelation(state, thisTaxon, lastTaxon, parentEvent.getLocation());
218 if (state.isTaxonInClassification() == true){
219 lastTaxon = thisTaxon;
220 // TODO for imports spanning multiple documents ?? Still needed?
221 state.getConfig().setLastTaxonUuid(lastTaxon.getUuid());
222 }
223 } else if (isStartingElement(next, ADDENDA)) {
224 handleNotYetImplementedElement(next);
225 } else {
226 handleUnexpectedElement(next);
227 }
228 }
229 return;
230 }
231
232 /**
233 * @param taxon
234 * @param lastTaxon
235 */
236 private void doTaxonRelation(MarkupImportState state, Taxon taxon, Taxon lastTaxon, Location dataLocation) {
237
238 if (state.isTaxonInClassification() == false){
239 return;
240 }
241
242 Classification tree = makeTree(state, dataLocation);
243 if (lastTaxon == null) {
244 tree.addChildTaxon(taxon, null, null);
245 return;
246 }
247 Rank thisRank = taxon.getName().getRank();
248 Rank lastRank = lastTaxon.getName().getRank();
249 if (lastRank == null){
250 String message = "Last rank was null. Can't create tree correctly";
251 fireWarningEvent(message, makeLocationStr(dataLocation), 12);
252 }
253 if (lastTaxon.getTaxonNodes().size() > 0) {
254 TaxonNode lastNode = lastTaxon.getTaxonNodes().iterator().next();
255 if (thisRank == null){
256 String message = "Rank is undefined for taxon '%s'. Can't create classification without rank.";
257 message = String.format(message, taxon.getName().getTitleCache());
258 fireWarningEvent(message, makeLocationStr(dataLocation), 6);
259 }else if (thisRank.isLower(lastRank)) {
260 lastNode.addChildTaxon(taxon, null, null);
261 fillMissingEpithetsForTaxa(lastTaxon, taxon);
262 } else if (thisRank.equals(lastRank)) {
263 TaxonNode parent = lastNode.getParent();
264 if (parent != null) {
265 parent.addChildTaxon(taxon, null, null);
266 fillMissingEpithetsForTaxa(parent.getTaxon(), taxon);
267 } else {
268 tree.addChildTaxon(taxon, null, null);
269 }
270 } else if (thisRank.isHigher(lastRank)) {
271 TaxonNode parent = lastNode.getParent();
272 if (parent != null){
273 doTaxonRelation(state, taxon, parent.getTaxon(), dataLocation);
274 }else{
275 String warning = "No parent available for lastNode. Classification can not be build correctly. Maybe the rank was missing for the lastNode";
276 fireWarningEvent(warning, makeLocationStr(dataLocation), 16);
277 //TODO what to do in this case (haven't spend time to think about yet
278 }
279 // TaxonNode parentNode = handleTaxonRelation(state, taxon,
280 // lastNode.getParent().getTaxon());
281 // parentNode.addChildTaxon(taxon, null, null, null);
282 }
283 } else {
284 String message = "Last taxon has no node";
285 fireWarningEvent(message, makeLocationStr(dataLocation), 6);
286 }
287 }
288
289
290
291 /**
292 * @param state
293 * @param dataLocation
294 * @return
295 */
296 private Classification makeTree(MarkupImportState state, Location dataLocation) {
297 Classification result = state.getTree(null);
298 if (result == null) {
299 UUID uuid = state.getConfig().getClassificationUuid();
300 if (uuid == null) {
301 String message = "No classification uuid is defined";
302 fireWarningEvent(message, makeLocationStr(dataLocation), 6);
303 result = createNewClassification(state);
304 } else {
305 result = getClassificationService().find(uuid);
306 if (result == null) {
307 result = createNewClassification(state);
308 result.setUuid(uuid);
309 }
310 }
311 state.putTree(null, result);
312 }
313 save(result, state);
314 return result;
315 }
316
317 private Classification createNewClassification(MarkupImportState state) {
318 Classification result = Classification.NewInstance(state.getConfig().getClassificationName(), getDefaultLanguage(state));
319 state.putTree(null, result);
320 return result;
321 }
322
323 private Taxon handleTaxon(MarkupImportState state, XMLEventReader reader, StartElement parentEvent) throws XMLStreamException {
324 // TODO progress monitoring
325 Map<String, Attribute> attributes = getAttributes(parentEvent);
326 Taxon taxon = createTaxonAndName(state, attributes);
327 state.setCurrentTaxon(taxon);
328 state.addNewFeatureSorterLists(taxon.getUuid().toString());
329
330 boolean hasTitle = false;
331 boolean hasNomenclature = false;
332 String taxonTitle = null;
333
334 Reference<?> descriptionReference = state.getConfig().getSourceReference();
335 while (reader.hasNext()) {
336 XMLEvent next = readNoWhitespace(reader);
337 if (next.isEndElement()) {
338 if (isMyEndingElement(next, parentEvent)) {
339 // checkMandatoryElement(hasTitle, parentEvent, TAXONTITLE);
340 checkMandatoryElement(hasNomenclature, parentEvent, NOMENCLATURE);
341 boolean inClassification = getAndRemoveBooleanAttributeValue(next, attributes, "inClassification", true);
342 state.setTaxonInClassification(inClassification);
343 handleUnexpectedAttributes(parentEvent.getLocation(),attributes);
344 if (taxon.getName().getRank() == null){
345 String warning = "No rank exists for taxon " + taxon.getTitleCache();
346 fireWarningEvent(warning, next, 12);
347 taxon.getName().setRank(Rank.UNKNOWN_RANK());
348 }
349
350 keyImport.makeKeyNodes(state, parentEvent, taxonTitle);
351 state.setCurrentTaxon(null);
352 state.setCurrentTaxonNum(null);
353 if (taxon.getName().getRank().isHigher(Rank.GENUS())){
354 state.setLatestGenusEpithet(null);
355 }else{
356 state.setLatestGenusEpithet(((NonViralName<?>)taxon.getName()).getGenusOrUninomial());
357 }
358 save(taxon, state);
359 return taxon;
360 } else {
361 if (isEndingElement(next, HEADING)) {
362 // NOT YET IMPLEMENTED
363 popUnimplemented(next.asEndElement());
364 } else if (isEndingElement(next, TEXT_SECTION)) {
365 // NOT YET IMPLEMENTED
366 popUnimplemented(next.asEndElement());
367 } else if (isEndingElement(next, REFERENCES)) {
368 // NOT YET IMPLEMENTED
369 popUnimplemented(next.asEndElement());
370 } else if (isEndingElement(next, FIGURE_REF)) {
371 // NOT YET IMPLEMENTED
372 popUnimplemented(next.asEndElement());
373 } else {
374 handleUnexpectedEndElement(next.asEndElement());
375 }
376 }
377 } else if (next.isStartElement()) {
378 if (isStartingElement(next, HEADING)) {
379 handleNotYetImplementedElement(next);
380 } else if (isStartingElement(next, TAXONTITLE)) {
381 taxonTitle = handleTaxonTitle(state, reader, next);
382 hasTitle = true;
383 } else if (isStartingElement(next, WRITER)) {
384 makeKeyWriter(state, reader, taxon, taxonTitle, next);
385 } else if (isStartingElement(next, TEXT_SECTION)) {
386 handleNotYetImplementedElement(next);
387 } else if (isStartingElement(next, KEY)) {
388 keyImport.handleKey(state, reader, next);
389 } else if (isStartingElement(next, NOMENCLATURE)) {
390 nomenclatureImport.handleNomenclature(state, reader, next);
391 hasNomenclature = true;
392 } else if (isStartingElement(next, FEATURE)) {
393 featureImport.handleFeature(state, reader, next);
394 } else if (isStartingElement(next, NOTES)) {
395 // TODO is this the correct way to handle notes?
396 String note = handleNotes(state, reader, next);
397
398 UUID notesUuid;
399 try {
400 notesUuid = state.getTransformer().getFeatureUuid("notes");
401 Feature feature = getFeature(state, notesUuid, "Notes", "Notes", "note", null);
402 TextData textData = TextData.NewInstance(feature);
403 textData.putText(getDefaultLanguage(state), note);
404 TaxonDescription description = getTaxonDescription(taxon, descriptionReference, false, true);
405 description.addElement(textData);
406 } catch (UndefinedTransformerMethodException e) {
407 String message = "getFeatureUuid method not yet implemented";
408 fireWarningEvent(message, next, 8);
409 }
410 } else if (isStartingElement(next, REFERENCES)) {
411 handleNotYetImplementedElement(next);
412 } else if (isStartingElement(next, FIGURE_REF)) {
413 TaxonDescription desc = getTaxonDescription(taxon, state.getConfig().getSourceReference(), IMAGE_GALLERY, CREATE_NEW);
414 TextData textData;
415 if (desc.getElements().isEmpty()){
416 textData = TextData.NewInstance(Feature.IMAGE());
417 desc.addElement(textData);
418 }
419 textData = (TextData)desc.getElements().iterator().next();
420 featureImport.makeFeatureFigureRef(state, reader, desc, false, textData, next);
421 } else if (isStartingElement(next, FIGURE)) {
422 handleFigure(state, reader, next, specimenImport, nomenclatureImport);
423 } else if (isStartingElement(next, FOOTNOTE)) {
424 FootnoteDataHolder footnote = handleFootnote(state, reader, next, specimenImport, nomenclatureImport);
425 if (footnote.isRef()) {
426 String message = "Ref footnote not implemented here";
427 fireWarningEvent(message, next, 4);
428 } else {
429 registerGivenFootnote(state, footnote);
430 }
431 } else {
432 handleUnexpectedStartElement(next);
433 }
434 } else {
435 handleUnexpectedElement(next);
436 }
437 }
438 throw new IllegalStateException("<Taxon> has no closing tag");
439 }
440
441 /**
442 * @param state
443 * @param reader
444 * @param taxon
445 * @param taxonTitle
446 * @param next
447 * @throws XMLStreamException
448 */
449 private void makeKeyWriter(MarkupImportState state, XMLEventReader reader, Taxon taxon, String taxonTitle, XMLEvent next) throws XMLStreamException {
450 WriterDataHolder writer = handleWriter(state, reader, next);
451 taxon.addExtension(writer.extension);
452 // TODO what if taxonTitle comes later
453 if (StringUtils.isNotBlank(taxonTitle) && writer.extension != null) {
454 Reference<?> sec = ReferenceFactory.newBookSection();
455 sec.setTitle(taxonTitle);
456 TeamOrPersonBase<?> author = createAuthor(writer.writer);
457 sec.setAuthorship(author);
458 sec.setInReference(state.getConfig().getSourceReference());
459 taxon.setSec(sec);
460 registerFootnotes(state, sec, writer.footnotes);
461 } else {
462 String message = "No taxontitle exists for writer";
463 fireWarningEvent(message, next, 6);
464 }
465 }
466
467 private String handleNotes(MarkupImportState state, XMLEventReader reader,
468 XMLEvent parentEvent) throws XMLStreamException {
469 checkNoAttributes(parentEvent);
470
471 String text = "";
472 while (reader.hasNext()) {
473 XMLEvent next = readNoWhitespace(reader);
474 if (isMyEndingElement(next, parentEvent)) {
475 return text;
476 } else if (next.isEndElement()) {
477 if (isEndingElement(next, HEADING)) {
478 popUnimplemented(next.asEndElement());
479 } else if (isEndingElement(next, WRITER)) {
480 popUnimplemented(next.asEndElement());
481 } else if (isEndingElement(next, NUM)) {
482 popUnimplemented(next.asEndElement());
483 } else {
484 handleUnexpectedEndElement(next.asEndElement());
485 }
486 } else if (next.isStartElement()) {
487 if (isStartingElement(next, HEADING)) {
488 handleNotYetImplementedElement(next);
489 } else if (isStartingElement(next, SUB_HEADING)) {
490 String subheading = getCData(state, reader, next).trim();
491 if (! isNoteHeading(subheading)) {
492 fireNotYetImplementedElement(next.getLocation(), next.asStartElement().getName(), 0);
493 }
494 } else if (isStartingElement(next, WRITER)) {
495 handleNotYetImplementedElement(next);
496 } else if (isStartingElement(next, NUM)) {
497 handleNotYetImplementedElement(next);
498 } else if (isStartingElement(next, STRING)) {
499 // TODO why multiple strings in schema?
500 text = makeNotesString(state, reader, text, next);
501 } else {
502 handleUnexpectedStartElement(next.asStartElement());
503 }
504 } else {
505 handleUnexpectedElement(next);
506 }
507 }
508 throw new IllegalStateException("<Notes> has no closing tag");
509 }
510
511 /**
512 * @param state
513 * @param reader
514 * @param text
515 * @param next
516 * @return
517 * @throws XMLStreamException
518 */
519 private String makeNotesString(MarkupImportState state, XMLEventReader reader, String text, XMLEvent next) throws XMLStreamException {
520 Map<String, String> stringMap = handleString(state, reader, next, null);
521 if (stringMap.size() == 0){
522 String message = "No text available in <notes>";
523 fireWarningEvent(message, next, 4);
524 }else if (stringMap.size() > 1){
525 String message = "Subheadings not yet supported in <notes>";
526 fireWarningEvent(message, next, 4);
527 }else{
528 String firstSubheading = stringMap.keySet().iterator().next();
529 if ( firstSubheading != null && ! isNoteHeading (firstSubheading) ) {
530 String message = "Subheadings not yet supported in <notes>";
531 fireWarningEvent(message, next, 4);
532 }
533 }
534 for (String subheading : stringMap.keySet()){
535 text += subheading;
536 text += stringMap.get(subheading);
537 }
538 return text;
539 }
540
541 private boolean isNoteHeading(String heading) {
542 String excludePattern = "(i?)(Notes?):?";
543 return heading.matches(excludePattern);
544 }
545
546 /**
547 * @param state
548 * @param attributes
549 */
550 private Taxon createTaxonAndName(MarkupImportState state,
551 Map<String, Attribute> attributes) {
552 NonViralName<?> name;
553 Rank rank = null; //Rank.SPECIES(); // default
554 boolean isCultivar = checkAndRemoveAttributeValue(attributes, CLASS, "cultivated");
555 if (isCultivar) {
556 name = CultivarPlantName.NewInstance(rank);
557 } else {
558 name = createNameByCode(state, rank);
559 }
560 Taxon taxon = Taxon.NewInstance(name, state.getConfig().getSourceReference());
561 if (checkAndRemoveAttributeValue(attributes, CLASS, "dubious")) {
562 taxon.setDoubtful(true);
563 } else if (checkAndRemoveAttributeValue(attributes, CLASS, "excluded")) {
564 taxon.setExcluded(true);
565 }
566 // TODO insufficient, new, expected
567 handleNotYetImplementedAttribute(attributes, CLASS);
568 // From old version
569 // MarkerType markerType = getMarkerType(state, attrValue);
570 // if (markerType == null){
571 // logger.warn("Class attribute value for taxon not yet supported: " +
572 // attrValue);
573 // }else{
574 // taxon.addMarker(Marker.NewInstance(markerType, true));
575 // }
576
577 // save(name, state);
578 // save(taxon, state);
579 return taxon;
580 }
581
582 private String handleTaxonTitle(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
583 //attributes
584 String text = "";
585 Map<String, Attribute> attributes = getAttributes(parentEvent);
586 String rankAttr = getAndRemoveAttributeValue(attributes, RANK);
587 Rank rank = makeRank(state, rankAttr, false);
588 String num = getAndRemoveAttributeValue(attributes, NUM);
589 state.setCurrentTaxonNum(num);
590 checkNoAttributes(attributes, parentEvent);
591
592 // TODO handle attributes
593 while (reader.hasNext()) {
594 XMLEvent next = readNoWhitespace(reader);
595 if (next.isEndElement()) {
596 if (isMyEndingElement(next, parentEvent)) {
597 Taxon taxon = state.getCurrentTaxon();
598 String titleText = null;
599 if (checkMandatoryText(text, parentEvent)) {
600 titleText = normalize(text);
601 UUID uuidTitle = MarkupTransformer.uuidTaxonTitle;
602 ExtensionType titleExtension = this.getExtensionType(state, uuidTitle, "Taxon Title ","taxon title", "title");
603 taxon.addExtension(titleText, titleExtension);
604 }
605 taxon.getName().setRank(rank);
606 // TODO check title exists
607 return titleText;
608 } else {
609 if (isEndingElement(next, FOOTNOTE)) {
610 // NOT YET IMPLEMENTED
611 popUnimplemented(next.asEndElement());
612 } else {
613 handleUnexpectedEndElement(next.asEndElement());
614 state.setUnsuccessfull();
615 }
616 }
617 } else if (next.isStartElement()) {
618 if (isStartingElement(next, FOOTNOTE)) {
619 handleNotYetImplementedElement(next);
620 }else if (isStartingElement(next, FOOTNOTE_REF)) {
621 handleNotYetImplementedElement(next);
622 } else {
623 handleUnexpectedStartElement(next);
624 state.setUnsuccessfull();
625 }
626 } else if (next.isCharacters()) {
627 text += next.asCharacters().getData();
628
629 } else {
630 handleUnexpectedElement(next);
631 state.setUnsuccessfull();
632 }
633 }
634 return null;
635
636 }
637
638
639 }