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