Markup import updates
[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.ArrayList;
15 import java.util.Arrays;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.UUID;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24
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;
31
32 import org.apache.commons.lang.StringUtils;
33 import org.apache.commons.lang.WordUtils;
34 import org.apache.log4j.Logger;
35
36 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
37 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade.DerivedUnitType;
38 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeCacheStrategy;
39 import eu.etaxonomy.cdm.common.CdmUtils;
40 import eu.etaxonomy.cdm.ext.geo.GeoServiceArea;
41 import eu.etaxonomy.cdm.ext.geo.IEditGeoService;
42 import eu.etaxonomy.cdm.io.common.CdmImportBase;
43 import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
44 import eu.etaxonomy.cdm.io.markup.UnmatchedLeads.UnmatchedLeadsKey;
45 import eu.etaxonomy.cdm.model.agent.AgentBase;
46 import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
47 import eu.etaxonomy.cdm.model.agent.Team;
48 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
49 import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
50 import eu.etaxonomy.cdm.model.common.Annotation;
51 import eu.etaxonomy.cdm.model.common.AnnotationType;
52 import eu.etaxonomy.cdm.model.common.CdmBase;
53 import eu.etaxonomy.cdm.model.common.Extension;
54 import eu.etaxonomy.cdm.model.common.ExtensionType;
55 import eu.etaxonomy.cdm.model.common.Language;
56 import eu.etaxonomy.cdm.model.common.TermVocabulary;
57 import eu.etaxonomy.cdm.model.common.TimePeriod;
58 import eu.etaxonomy.cdm.model.description.CommonTaxonName;
59 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
60 import eu.etaxonomy.cdm.model.description.Distribution;
61 import eu.etaxonomy.cdm.model.description.Feature;
62 import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
63 import eu.etaxonomy.cdm.model.description.KeyStatement;
64 import eu.etaxonomy.cdm.model.description.PolytomousKey;
65 import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
66 import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;
67 import eu.etaxonomy.cdm.model.description.PresenceTerm;
68 import eu.etaxonomy.cdm.model.description.TaxonDescription;
69 import eu.etaxonomy.cdm.model.description.TextData;
70 import eu.etaxonomy.cdm.model.location.NamedArea;
71 import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
72 import eu.etaxonomy.cdm.model.location.NamedAreaType;
73 import eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity;
74 import eu.etaxonomy.cdm.model.media.Media;
75 import eu.etaxonomy.cdm.model.name.CultivarPlantName;
76 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
77 import eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus;
78 import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
79 import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
80 import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
81 import eu.etaxonomy.cdm.model.name.NonViralName;
82 import eu.etaxonomy.cdm.model.name.Rank;
83 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
84 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
85 import eu.etaxonomy.cdm.model.occurrence.Collection;
86 import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
87 import eu.etaxonomy.cdm.model.occurrence.FieldObservation;
88 import eu.etaxonomy.cdm.model.occurrence.Specimen;
89 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
90 import eu.etaxonomy.cdm.model.reference.IArticle;
91 import eu.etaxonomy.cdm.model.reference.IJournal;
92 import eu.etaxonomy.cdm.model.reference.Reference;
93 import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
94 import eu.etaxonomy.cdm.model.reference.ReferenceType;
95 import eu.etaxonomy.cdm.model.taxon.Classification;
96 import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
97 import eu.etaxonomy.cdm.model.taxon.Taxon;
98 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
99 import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
100 import eu.etaxonomy.cdm.strategy.parser.NameTypeParser;
101 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
102 import eu.etaxonomy.cdm.strategy.parser.SpecimenTypeParser;
103 import eu.etaxonomy.cdm.strategy.parser.SpecimenTypeParser.TypeInfo;
104
105 /**
106 * @author a.mueller
107 *
108 */
109 public class MarkupDocumentImportNoComponent extends MarkupImportBase {
110 private static final Logger logger = Logger.getLogger(MarkupDocumentImportNoComponent.class);
111
112 private static final boolean CREATE_NEW = true;
113 private static final boolean IS_IMAGE_GALLERY = true;
114 private static final boolean NO_IMAGE_GALLERY = false;
115
116
117 private static final String ACCEPTED = "accepted";
118 private static final String ACCEPTED_NAME = "acceptedName";
119 private static final String ADDENDA = "addenda";
120 private static final String ALTERNATEPUBTITLE = "alternatepubtitle";
121 private static final String ALTERNATIVE_COLLECTION_TYPE_STATUS = "alternativeCollectionTypeStatus";
122 private static final String ALTERNATIVE_COLLECTOR = "alternativeCollector";
123 private static final String ALTERNATIVE_FIELD_NUM = "alternativeFieldNum";
124 private static final String ALTITUDE = "altitude";
125 private static final String ANNOTATION = "annotation";
126 private static final String AUTHOR = "author";
127 private static final String BIBLIOGRAPHY = "bibliography";
128 private static final String BIOGRAPHIES = "biographies";
129 private static final String BOLD = "bold";
130 private static final String BR = "br";
131 private static final String CHAR = "char";
132 private static final String CITATION = "citation";
133 private static final String COLLECTION_AND_TYPE = "collectionAndType";
134 private static final String COLLECTION_TYPE_STATUS = "collectionTypeStatus";
135 private static final String COLLECTOR = "collector";
136 private static final String COLLECTION = "collection";
137 private static final String COORDINATES = "coordinates";
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 ISSUE = "issue";
171 private static final String ITALICS = "italics";
172 private static final String KEY = "key";
173 private static final String LIFE_CYCLE_PERIODS = "lifeCyclePeriods";
174 private static final String LOCALITY = "locality";
175 private static final String LOST = "lost";
176 private static final String META_DATA = "metaData";
177 private static final String NAME = "name";
178 private static final String NAME_TYPE = "nameType";
179 private static final String NOM = "nom";
180 private static final String NOMENCLATURE = "nomenclature";
181 private static final String NOT_FOUND = "notFound";
182 private static final String NOT_SEEN = "notSeen";
183 private static final String NOTES = "notes";
184 private static final String ORIGINAL_DETERMINATION = "originalDetermination";
185 private static final String PAGES = "pages";
186 private static final String PARAUT = "paraut";
187 private static final String PUBFULLNAME = "pubfullname";
188 private static final String PUBLICATION = "publication";
189 private static final String PUBNAME = "pubname";
190 private static final String PUBTITLE = "pubtitle";
191 private static final String PUBTYPE = "pubtype";
192 private static final String QUOTE = "quote";
193 private static final String RANK = "rank";
194 private static final String REF = "ref";
195 private static final String REF_NUM = "refNum";
196 private static final String REF_PART = "refPart";
197 private static final String REFERENCE = "reference";
198 private static final String REFERENCES = "references";
199 private static final String TAXON = "taxon";
200 private static final String TAXONTITLE = "taxontitle";
201 private static final String TAXONTYPE = "taxontype";
202 private static final String TEXT_SECTION = "textSection";
203 private static final String TYPE = "type";
204 private static final String TYPE_STATUS = "typeStatus";
205 private static final String TREATMENT = "treatment";
206 private static final String SERIALS_ABBREVIATIONS = "serialsAbbreviations";
207 private static final String SPECIMEN_TYPE = "specimenType";
208 private static final String STATUS = "status";
209 private static final String STRING = "string";
210 private static final String SUB_HEADING = "subHeading";
211 private static final String SUB_COLLECTION = "subCollection";
212 private static final String SYNONYM = "synonym";
213 private static final String UNKNOWN = "unknown";
214 private static final String URL = "url";
215 private static final String USAGE = "usage";
216 private static final String VOLUME = "volume";
217 private static final String WRITER = "writer";
218 private static final String YEAR = "year";
219
220 private NonViralNameParserImpl parser = new NonViralNameParserImpl();
221
222 // TODO make part of state, but state is renewed when invoking the import a
223 // second time
224 private UnmatchedLeads unmatchedLeads;
225
226
227 private IEditGeoService editGeoService;
228
229 private MarkupKeyImport keyImport;
230
231 public MarkupDocumentImportNoComponent(MarkupDocumentImport docImport) {
232 super(docImport);
233 this.editGeoService = docImport.getEditGeoService();
234 keyImport = new MarkupKeyImport(docImport);
235 }
236
237 public void doInvoke(MarkupImportState state) throws XMLStreamException {
238 XMLEventReader reader = state.getReader();
239
240 // publication (= root element)
241 String elName = PUBLICATION;
242 boolean hasPublication = false;
243
244 while (reader.hasNext()) {
245 XMLEvent nextEvent = reader.nextEvent();
246 if (isStartingElement(nextEvent, elName)) {
247 handlePublication(state, reader, nextEvent, elName);
248 hasPublication = true;
249 } else if (nextEvent.isEndDocument()) {
250 if (!hasPublication) {
251 String message = "No publication root element found";
252 fireWarningEvent(message, nextEvent, 8);
253 }
254 // done
255 } else {
256 fireSchemaConflictEventExpectedStartTag(elName, reader);
257 }
258 }
259
260
261 return;
262
263 }
264
265 private void handlePublication(MarkupImportState state, XMLEventReader reader, XMLEvent currentEvent, String elName) throws XMLStreamException {
266
267 // attributes
268 StartElement element = currentEvent.asStartElement();
269 Map<String, Attribute> attributes = getAttributes(element);
270 String lang = getAndRemoveAttributeValue(attributes, "lang");
271 if (lang != null){
272 Language language = getTermService().getLanguageByIso(lang);
273 state.setDefaultLanguage(language);
274 }
275
276 handleUnexpectedAttributes(element.getLocation(), attributes, "noNamespaceSchemaLocation");
277
278 while (reader.hasNext()) {
279 XMLEvent event = readNoWhitespace(reader);
280 // TODO cardinality of alternative
281 if (event.isEndElement()) {
282 if (isEndingElement(event, elName)) {
283 return;
284 } else {
285 if (isEndingElement(event, BIOGRAPHIES)) {
286 // NOT YET IMPLEMENTED
287 popUnimplemented(event.asEndElement());
288 } else if (isEndingElement(event, REFERENCES)) {
289 // NOT YET IMPLEMENTED
290 popUnimplemented(event.asEndElement());
291 } else if (isEndingElement(event, TEXT_SECTION)) {
292 // NOT YET IMPLEMENTED
293 popUnimplemented(event.asEndElement());
294 } else if (isEndingElement(event, ADDENDA)) {
295 // NOT YET IMPLEMENTED
296 popUnimplemented(event.asEndElement());
297 } else {
298 handleUnexpectedElement(event);
299 }
300 }
301 } else if (event.isStartElement()) {
302 if (isStartingElement(event, META_DATA)) {
303 handleMetaData(state, reader, event);
304 } else if (isStartingElement(event, TREATMENT)) {
305 handleTreatment(state, reader, event);
306 } else if (isStartingElement(event, BIOGRAPHIES)) {
307 handleNotYetImplementedElement(event);
308 } else if (isStartingElement(event, REFERENCES)) {
309 handleNotYetImplementedElement(event);
310 } else if (isStartingElement(event, TEXT_SECTION)) {
311 handleNotYetImplementedElement(event);
312 } else if (isStartingElement(event, ADDENDA)) {
313 handleNotYetImplementedElement(event);
314 } else {
315 handleUnexpectedStartElement(event);
316 }
317 } else {
318 handleUnexpectedElement(event);
319 }
320 }
321 throw new IllegalStateException("Publication has no ending element");
322 }
323
324 private void handleMetaData(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
325 checkNoAttributes(parentEvent);
326
327 while (reader.hasNext()) {
328 XMLEvent next = readNoWhitespace(reader);
329 if (isMyEndingElement(next, parentEvent)) {
330 return;
331 } else if (isStartingElement(next, DEFAULT_MEDIA_URL)) {
332 String baseUrl = getCData(state, reader, next);
333 try {
334 new URL(baseUrl);
335 state.setBaseMediaUrl(baseUrl);
336 } catch (MalformedURLException e) {
337 String message = "defaultMediaUrl '%s' is not a valid URL";
338 message = String.format(message, baseUrl);
339 fireWarningEvent(message, next, 8);
340 }
341 } else {
342 handleUnexpectedElement(next);
343 }
344 }
345 throw new IllegalStateException("MetaData has no ending element");
346
347 }
348
349 private void handleTreatment(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
350 checkNoAttributes(parentEvent);
351 Taxon lastTaxon = null;
352 while (reader.hasNext()) {
353 XMLEvent next = readNoWhitespace(reader);
354 if (isStartingElement(next, TAXON)) {
355 Taxon thisTaxon = handleTaxon(state, reader, next.asStartElement());
356 doTaxonRelation(state, thisTaxon, lastTaxon, parentEvent.getLocation());
357 lastTaxon = thisTaxon;
358 // TODO for imports spanning multiple documents ?? Still needed?
359 state.getConfig().setLastTaxonUuid(lastTaxon.getUuid());
360 } else if (isMyEndingElement(next, parentEvent)) {
361 Set<PolytomousKeyNode> keyNodesToSave = state.getPolytomousKeyNodesToSave();
362 //better save the key then the nodes
363 Set<PolytomousKey> keySet = new HashSet<PolytomousKey>();
364 for (PolytomousKeyNode node : keyNodesToSave){
365 PolytomousKey key = node.getKey();
366 keySet.add(key);
367 }
368 save(keySet, state);
369 //unmatched key leads
370 UnmatchedLeads unmatched = state.getUnmatchedLeads();
371 if (unmatched.size() > 0){
372 String message = "The following key leads are unmatched: %s";
373 message = String.format(message, state.getUnmatchedLeads().toString());
374 fireWarningEvent(message, next, 6);
375 }
376 // save(keyNodesToSave, state);
377
378 return;
379 } else {
380 handleUnexpectedElement(next);
381 }
382 }
383 return;
384 }
385
386 /**
387 * @param taxon
388 * @param lastTaxon
389 */
390 private void doTaxonRelation(MarkupImportState state, Taxon taxon,
391 Taxon lastTaxon, Location dataLocation) {
392
393 Classification tree = makeTree(state, dataLocation);
394 if (lastTaxon == null) {
395 tree.addChildTaxon(taxon, null, null, null);
396 return;
397 }
398 Rank thisRank = taxon.getName().getRank();
399 Rank lastRank = lastTaxon.getName().getRank();
400 if (lastTaxon.getTaxonNodes().size() > 0) {
401 TaxonNode lastNode = lastTaxon.getTaxonNodes().iterator().next();
402 if (thisRank == null){
403 String message = "rank is undefined for taxon '%s'. Can't create classification without rank.";
404 message = String.format(message, taxon.getName().getTitleCache());
405 fireWarningEvent(message, makeLocationStr(dataLocation), 6);
406 }else if (thisRank.isLower(lastRank)) {
407 lastNode.addChildTaxon(taxon, null, null, null);
408 fillMissingEpithetsForTaxa(lastTaxon, taxon);
409 } else if (thisRank.equals(lastRank)) {
410 TaxonNode parent = lastNode.getParent();
411 if (parent != null) {
412 parent.addChildTaxon(taxon, null, null, null);
413 fillMissingEpithetsForTaxa(parent.getTaxon(), taxon);
414 } else {
415 tree.addChildTaxon(taxon, null, null, null);
416 }
417 } else if (thisRank.isHigher(lastRank)) {
418 doTaxonRelation(state, taxon, lastNode.getParent().getTaxon(), dataLocation);
419 // TaxonNode parentNode = handleTaxonRelation(state, taxon,
420 // lastNode.getParent().getTaxon());
421 // parentNode.addChildTaxon(taxon, null, null, null);
422 }
423 } else {
424
425 String message = "Last taxon has no node";
426 fireWarningEvent(message, makeLocationStr(dataLocation), 6);
427 }
428 }
429
430
431
432 /**
433 * @param state
434 * @param dataLocation
435 * @return
436 */
437 private Classification makeTree(MarkupImportState state, Location dataLocation) {
438 Classification result = state.getTree(null);
439 if (result == null) {
440 UUID uuid = state.getConfig().getClassificationUuid();
441 if (uuid == null) {
442 String message = "No classification uuid is defined";
443 fireWarningEvent(message, makeLocationStr(dataLocation), 6);
444 result = createNewClassification(state);
445 } else {
446 result = getClassificationService().find(uuid);
447 if (result == null) {
448 result = createNewClassification(state);
449 result.setUuid(uuid);
450 }
451 }
452 state.putTree(null, result);
453 }
454 save(result, state);
455 return result;
456 }
457
458 private Classification createNewClassification(MarkupImportState state) {
459 Classification result;
460 result = Classification.NewInstance(state.getConfig().getClassificationTitle());
461 state.putTree(null, result);
462 return result;
463 }
464
465 private Taxon handleTaxon(MarkupImportState state, XMLEventReader reader, StartElement parentEvent) throws XMLStreamException {
466 // TODO progress monitoring
467 Map<String, Attribute> attributes = getAttributes(parentEvent);
468 Taxon taxon = createTaxonAndName(state, attributes);
469 state.setCurrentTaxon(taxon);
470
471 boolean hasTitle = false;
472 boolean hasNomenclature = false;
473 String taxonTitle = null;
474
475 while (reader.hasNext()) {
476 XMLEvent next = readNoWhitespace(reader);
477 if (next.isEndElement()) {
478 if (isMyEndingElement(next, parentEvent)) {
479 checkMandatoryElement(hasTitle, parentEvent, TAXONTITLE);
480 checkMandatoryElement(hasNomenclature, parentEvent, NOMENCLATURE);
481 handleUnexpectedAttributes(parentEvent.getLocation(),attributes);
482 if (taxon.getName().getRank() == null){
483 String warning = "No rank exists for taxon " + taxon.getTitleCache();
484 fireWarningEvent(warning, next, 12);
485 }
486
487 keyImport.makeKeyNodes(state, parentEvent, taxonTitle);
488 state.setCurrentTaxon(null);
489 state.setCurrentTaxonNum(null);
490 save(taxon, state);
491 return taxon;
492 } else {
493 if (isEndingElement(next, HEADING)) {
494 // NOT YET IMPLEMENTED
495 popUnimplemented(next.asEndElement());
496 } else if (isEndingElement(next, TEXT_SECTION)) {
497 // NOT YET IMPLEMENTED
498 popUnimplemented(next.asEndElement());
499 } else if (isEndingElement(next, REFERENCES)) {
500 // NOT YET IMPLEMENTED
501 popUnimplemented(next.asEndElement());
502 } else if (isEndingElement(next, FIGURE_REF)) {
503 // NOT YET IMPLEMENTED
504 popUnimplemented(next.asEndElement());
505 } else {
506 handleUnexpectedEndElement(next.asEndElement());
507 }
508 }
509 } else if (next.isStartElement()) {
510 if (isStartingElement(next, HEADING)) {
511 handleNotYetImplementedElement(next);
512 } else if (isStartingElement(next, TAXONTITLE)) {
513 taxonTitle = handleTaxonTitle(state, reader, next);
514 hasTitle = true;
515 } else if (isStartingElement(next, WRITER)) {
516 makeKeyWriter(state, reader, taxon, taxonTitle, next);
517 } else if (isStartingElement(next, TEXT_SECTION)) {
518 handleNotYetImplementedElement(next);
519 } else if (isStartingElement(next, KEY)) {
520 keyImport.handleKey(state, reader, next);
521 } else if (isStartingElement(next, NOMENCLATURE)) {
522 handleNomenclature(state, reader, next);
523 hasNomenclature = true;
524 } else if (isStartingElement(next, FEATURE)) {
525 handleFeature(state, reader, next);
526 } else if (isStartingElement(next, NOTES)) {
527 // TODO is this the correct way to handle notes?
528 String note = handleNotes(state, reader, next);
529
530 UUID notesUuid;
531 try {
532 notesUuid = state.getTransformer().getFeatureUuid(
533 "notes");
534 Feature feature = getFeature(state, notesUuid, "Notes",
535 "Notes", "note", null);
536 TextData textData = TextData.NewInstance(feature);
537 textData.putText(Language.DEFAULT(), note);
538 TaxonDescription description = getTaxonDescription(
539 taxon, null, false, true);
540 description.addElement(textData);
541 } catch (UndefinedTransformerMethodException e) {
542 String message = "getFeatureUuid method not yet implemented";
543 fireWarningEvent(message, next, 8);
544 }
545 } else if (isStartingElement(next, REFERENCES)) {
546 handleNotYetImplementedElement(next);
547 } else if (isStartingElement(next, FIGURE_REF)) {
548 handleNotYetImplementedElement(next);
549 } else if (isStartingElement(next, FIGURE)) {
550 handleFigure(state, reader, next);
551 } else if (isStartingElement(next, FOOTNOTE)) {
552 FootnoteDataHolder footnote = handleFootnote(state, reader, next);
553 if (footnote.isRef()) {
554 String message = "Ref footnote not implemented here";
555 fireWarningEvent(message, next, 4);
556 } else {
557 registerGivenFootnote(state, footnote);
558 }
559 } else {
560 handleUnexpectedStartElement(next);
561 }
562 } else {
563 handleUnexpectedElement(next);
564 }
565 }
566 throw new IllegalStateException("<Taxon> has no closing tag");
567 }
568
569 /**
570 * @param state
571 * @param reader
572 * @param taxon
573 * @param taxonTitle
574 * @param next
575 * @throws XMLStreamException
576 */
577 private void makeKeyWriter(MarkupImportState state, XMLEventReader reader, Taxon taxon, String taxonTitle, XMLEvent next) throws XMLStreamException {
578 WriterDataHolder writer = handleWriter(state, reader, next);
579 taxon.addExtension(writer.extension);
580 // TODO what if taxonTitle comes later
581 if (StringUtils.isNotBlank(taxonTitle)
582 && writer.extension != null) {
583 Reference<?> sec = ReferenceFactory.newBookSection();
584 sec.setTitle(taxonTitle);
585 TeamOrPersonBase<?> author = createAuthor(writer.writer);
586 sec.setAuthorTeam(author);
587 sec.setInReference(state.getConfig()
588 .getSourceReference());
589 taxon.setSec(sec);
590 registerFootnotes(state, sec, writer.footnotes);
591 } else {
592 String message = "No taxontitle exists for writer";
593 fireWarningEvent(message, next, 6);
594 }
595 }
596
597 private String handleNotes(MarkupImportState state, XMLEventReader reader,
598 XMLEvent parentEvent) throws XMLStreamException {
599 checkNoAttributes(parentEvent);
600
601 String text = "";
602 while (reader.hasNext()) {
603 XMLEvent next = readNoWhitespace(reader);
604 if (isMyEndingElement(next, parentEvent)) {
605 return text;
606 } else if (next.isEndElement()) {
607 if (isEndingElement(next, HEADING)) {
608 popUnimplemented(next.asEndElement());
609 } else if (isEndingElement(next, WRITER)) {
610 popUnimplemented(next.asEndElement());
611 } else if (isEndingElement(next, NUM)) {
612 popUnimplemented(next.asEndElement());
613 } else {
614 handleUnexpectedEndElement(next.asEndElement());
615 }
616 } else if (next.isStartElement()) {
617 if (isStartingElement(next, HEADING)) {
618 handleNotYetImplementedElement(next);
619 } else if (isStartingElement(next, SUB_HEADING)) {
620 String subheading = getCData(state, reader, next).trim();
621 if (! isNoteHeading(subheading)) {
622 fireNotYetImplementedElement(next.getLocation(), next.asStartElement().getName(), 0);
623 }
624 } else if (isStartingElement(next, WRITER)) {
625 handleNotYetImplementedElement(next);
626 } else if (isStartingElement(next, NUM)) {
627 handleNotYetImplementedElement(next);
628 } else if (isStartingElement(next, STRING)) {
629 // TODO why multiple strings in schema?
630 text = makeNotesString(state, reader, text, next);
631 } else {
632 handleUnexpectedStartElement(next.asStartElement());
633 }
634 } else {
635 handleUnexpectedElement(next);
636 }
637 }
638 throw new IllegalStateException("<Notes> has no closing tag");
639 }
640
641 /**
642 * @param state
643 * @param reader
644 * @param text
645 * @param next
646 * @return
647 * @throws XMLStreamException
648 */
649 private String makeNotesString(MarkupImportState state, XMLEventReader reader, String text, XMLEvent next) throws XMLStreamException {
650 Map<String, String> stringMap = handleString(state, reader, next, null);
651 if (stringMap.size() == 0){
652 String message = "No text available in <notes>";
653 fireWarningEvent(message, next, 4);
654 }else if (stringMap.size() > 1){
655 String message = "Subheadings not yet supported in <notes>";
656 fireWarningEvent(message, next, 4);
657 }else{
658 String firstSubheading = stringMap.keySet().iterator().next();
659 if ( firstSubheading != null && ! isNoteHeading (firstSubheading) ) {
660 String message = "Subheadings not yet supported in <notes>";
661 fireWarningEvent(message, next, 4);
662 }
663 }
664 for (String subheading : stringMap.keySet()){
665 text += subheading;
666 text += stringMap.get(subheading);
667 }
668 return text;
669 }
670
671 private boolean isNoteHeading(String heading) {
672 String excludePattern = "(i?)(Notes?):?";
673 return heading.matches(excludePattern);
674 }
675
676 /**
677 * @param state
678 * @param attributes
679 */
680 private Taxon createTaxonAndName(MarkupImportState state,
681 Map<String, Attribute> attributes) {
682 NonViralName<?> name;
683 Rank rank = null; //Rank.SPECIES(); // default
684 boolean isCultivar = checkAndRemoveAttributeValue(attributes, CLASS, "cultivated");
685 if (isCultivar) {
686 name = CultivarPlantName.NewInstance(rank);
687 } else {
688 name = createNameByCode(state, rank);
689 }
690 Taxon taxon = Taxon.NewInstance(name, state.getConfig().getSourceReference());
691 if (checkAndRemoveAttributeValue(attributes, CLASS, "dubious")) {
692 taxon.setDoubtful(true);
693 } else if (checkAndRemoveAttributeValue(attributes, CLASS, "excluded")) {
694 taxon.setExcluded(true);
695 }
696 // TODO insufficient, new, expected
697 handleNotYetImplementedAttribute(attributes, CLASS);
698 // From old version
699 // MarkerType markerType = getMarkerType(state, attrValue);
700 // if (markerType == null){
701 // logger.warn("Class attribute value for taxon not yet supported: " +
702 // attrValue);
703 // }else{
704 // taxon.addMarker(Marker.NewInstance(markerType, true));
705 // }
706
707 // save(name, state);
708 // save(taxon, state);
709 return taxon;
710 }
711
712 /**
713 * @param state
714 * @param rank
715 * @return
716 */
717 private NonViralName<?> createNameByCode(MarkupImportState state, Rank rank) {
718 NonViralName<?> name;
719 NomenclaturalCode nc = makeNomenclaturalCode(state);
720 name = (NonViralName<?>) nc.getNewTaxonNameInstance(rank);
721 return name;
722 }
723
724 /**
725 * @param state
726 * @return
727 */
728 private NomenclaturalCode makeNomenclaturalCode(MarkupImportState state) {
729 NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
730 if (nc == null) {
731 nc = NomenclaturalCode.ICBN; // default;
732 }
733 return nc;
734 }
735
736 private String handleTaxonTitle(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
737 //attributes
738 String text = "";
739 Map<String, Attribute> attributes = getAttributes(parentEvent);
740 String rankAttr = getAndRemoveAttributeValue(attributes, RANK);
741 Rank rank = makeRank(state, rankAttr, false);
742 String num = getAndRemoveAttributeValue(attributes, NUM);
743 state.setCurrentTaxonNum(num);
744 checkNoAttributes(attributes, parentEvent);
745
746 // TODO handle attributes
747 while (reader.hasNext()) {
748 XMLEvent next = readNoWhitespace(reader);
749 if (next.isEndElement()) {
750 if (isMyEndingElement(next, parentEvent)) {
751 Taxon taxon = state.getCurrentTaxon();
752 String titleText = null;
753 if (checkMandatoryText(text, parentEvent)) {
754 titleText = normalize(text);
755 UUID uuidTitle = MarkupTransformer.uuidTaxonTitle;
756 ExtensionType titleExtension = this.getExtensionType(state, uuidTitle, "Taxon Title ","taxon title", "title");
757 taxon.addExtension(titleText, titleExtension);
758 }
759 taxon.getName().setRank(rank);
760 // TODO check title exists
761 return titleText;
762 } else {
763 if (isEndingElement(next, FOOTNOTE)) {
764 // NOT YET IMPLEMENTED
765 popUnimplemented(next.asEndElement());
766 } else {
767 handleUnexpectedEndElement(next.asEndElement());
768 state.setUnsuccessfull();
769 }
770 }
771 } else if (next.isStartElement()) {
772 if (isStartingElement(next, FOOTNOTE)) {
773 handleNotYetImplementedElement(next);
774 } else {
775 handleUnexpectedStartElement(next);
776 state.setUnsuccessfull();
777 }
778 } else if (next.isCharacters()) {
779 text += next.asCharacters().getData();
780
781 } else {
782 handleUnexpectedElement(next);
783 state.setUnsuccessfull();
784 }
785 }
786 return null;
787
788 }
789
790 private WriterDataHolder handleWriter(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
791 String text = "";
792 checkNoAttributes(parentEvent);
793 WriterDataHolder dataHolder = new WriterDataHolder();
794 List<FootnoteDataHolder> footnotes = new ArrayList<FootnoteDataHolder>();
795
796 // TODO handle attributes
797 while (reader.hasNext()) {
798 XMLEvent next = readNoWhitespace(reader);
799 if (isMyEndingElement(next, parentEvent)) {
800 text = CdmUtils.removeBrackets(text);
801 if (checkMandatoryText(text, parentEvent)) {
802 text = normalize(text);
803 dataHolder.writer = text;
804 dataHolder.footnotes = footnotes;
805
806 // Extension
807 UUID uuidWriterExtension = MarkupTransformer.uuidWriterExtension;
808 ExtensionType writerExtensionType = this
809 .getExtensionType(state, uuidWriterExtension,
810 "Writer", "writer", "writer");
811 Extension extension = Extension.NewInstance();
812 extension.setType(writerExtensionType);
813 extension.setValue(text);
814 dataHolder.extension = extension;
815
816 // Annotation
817 UUID uuidWriterAnnotation = MarkupTransformer.uuidWriterAnnotation;
818 AnnotationType writerAnnotationType = this.getAnnotationType(state, uuidWriterAnnotation, "Writer", "writer", "writer", null);
819 Annotation annotation = Annotation.NewInstance(text, writerAnnotationType, Language.DEFAULT());
820 dataHolder.annotation = annotation;
821
822 return dataHolder;
823 } else {
824 return null;
825 }
826 } else if (isStartingElement(next, FOOTNOTE_REF)) {
827 FootnoteDataHolder footNote = handleFootnoteRef(state, reader, next);
828 if (footNote.isRef()) {
829 footnotes.add(footNote);
830 } else {
831 logger.warn("Non ref footnotes not yet impelemnted");
832 }
833 } else if (next.isCharacters()) {
834 text += next.asCharacters().getData();
835
836 } else {
837 handleUnexpectedElement(next);
838 state.setUnsuccessfull();
839 }
840 }
841 throw new IllegalStateException("<writer> has no end tag");
842 }
843
844 private void registerFootnotes(MarkupImportState state, AnnotatableEntity entity, List<FootnoteDataHolder> footnotes) {
845 for (FootnoteDataHolder footNote : footnotes) {
846 registerFootnoteDemand(state, entity, footNote);
847 }
848 }
849
850 private void registerGivenFootnote(MarkupImportState state, FootnoteDataHolder footnote) {
851 state.registerFootnote(footnote);
852 Set<AnnotatableEntity> demands = state.getFootnoteDemands(footnote.id);
853 if (demands != null) {
854 for (AnnotatableEntity entity : demands) {
855 attachFootnote(state, entity, footnote);
856 }
857 }
858 }
859
860 private void registerGivenFigure(MarkupImportState state, String id, Media figure) {
861 state.registerFigure(id, figure);
862 Set<AnnotatableEntity> demands = state.getFigureDemands(id);
863 if (demands != null) {
864 for (AnnotatableEntity entity : demands) {
865 attachFigure(state, entity, figure);
866 }
867 }
868 }
869
870 private void registerFootnoteDemand(MarkupImportState state, AnnotatableEntity entity, FootnoteDataHolder footnote) {
871 FootnoteDataHolder existingFootnote = state.getFootnote(footnote.ref);
872 if (existingFootnote != null) {
873 attachFootnote(state, entity, existingFootnote);
874 } else {
875 Set<AnnotatableEntity> demands = state.getFootnoteDemands(footnote.ref);
876 if (demands == null) {
877 demands = new HashSet<AnnotatableEntity>();
878 state.putFootnoteDemands(footnote.ref, demands);
879 }
880 demands.add(entity);
881 }
882 }
883
884 private void registerFigureDemand(MarkupImportState state, AnnotatableEntity entity, String figureRef) {
885 Media existingFigure = state.getFigure(figureRef);
886 if (existingFigure != null) {
887 attachFigure(state, entity, existingFigure);
888 } else {
889 Set<AnnotatableEntity> demands = state.getFigureDemands(figureRef);
890 if (demands == null) {
891 demands = new HashSet<AnnotatableEntity>();
892 state.putFigureDemands(figureRef, demands);
893 }
894 demands.add(entity);
895 }
896 }
897
898 private void attachFootnote(MarkupImportState state, AnnotatableEntity entity, FootnoteDataHolder footnote) {
899 AnnotationType annotationType = this.getAnnotationType(state, MarkupTransformer.uuidFootnote, "Footnote", "An e-flora footnote", "fn", null);
900 Annotation annotation = Annotation.NewInstance(footnote.string,
901 annotationType, Language.DEFAULT());
902 // TODO transient objects
903 entity.addAnnotation(annotation);
904 save(entity, state);
905 }
906
907 private void attachFigure(MarkupImportState state,
908 AnnotatableEntity entity, Media figure) {
909 // IdentifiableEntity<?> toSave;
910 if (entity.isInstanceOf(TextData.class)) {
911 TextData deb = CdmBase.deproxy(entity, TextData.class);
912 deb.addMedia(figure);
913 // toSave = ((TaxonDescription)deb.getInDescription()).getTaxon();
914 } else if (entity.isInstanceOf(IdentifiableMediaEntity.class)) {
915 IdentifiableMediaEntity<?> ime = CdmBase.deproxy(entity,
916 IdentifiableMediaEntity.class);
917 ime.addMedia(figure);
918 // toSave = ime;
919 } else {
920 String message = "Unsupported entity to attach media: %s";
921 message = String.format(message, entity.getClass().getName());
922 // toSave = null;
923 }
924 save(entity, state);
925 }
926
927 private void handleFigure(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
928 // FigureDataHolder result = new FigureDataHolder();
929
930 Map<String, Attribute> attributes = getAttributes(parentEvent);
931 String id = getAndRemoveAttributeValue(attributes, ID);
932 String type = getAndRemoveAttributeValue(attributes, TYPE);
933 checkNoAttributes(attributes, parentEvent);
934
935 String urlString = null;
936 String legendString = null;
937 String titleString = null;
938 String numString = null;
939 String text = null;
940 while (reader.hasNext()) {
941 XMLEvent next = readNoWhitespace(reader);
942 if (isMyEndingElement(next, parentEvent)) {
943 makeFigure(state, id, type, urlString, legendString, titleString, numString, next);
944 return;
945 } else if (isStartingElement(next, FIGURE_LEGEND)) {
946 // TODO same as figurestring ?
947 legendString = handleFootnoteString(state, reader, next);
948 } else if (isStartingElement(next, FIGURE_TITLE)) {
949 titleString = getCData(state, reader, next);
950 } else if (isStartingElement(next, URL)) {
951 String localUrl = getCData(state, reader, next);
952 urlString = CdmUtils.Nz(state.getBaseMediaUrl()) + localUrl;
953 } else if (isStartingElement(next, NUM)) {
954 numString = getCData(state, reader, next);
955 } else if (next.isCharacters()) {
956 text += next.asCharacters().getData();
957 } else {
958 fireUnexpectedEvent(next, 0);
959 }
960 }
961 throw new IllegalStateException("<figure> has no end tag");
962 }
963
964 /**
965 * @param state
966 * @param id
967 * @param type
968 * @param urlString
969 * @param legendString
970 * @param titleString
971 * @param numString
972 * @param next
973 */
974 private void makeFigure(MarkupImportState state, String id, String type, String urlString,
975 String legendString, String titleString, String numString, XMLEvent next) {
976 Media media = null;
977 boolean isFigure = false;
978 try {
979 //TODO maybe everything is a figure as it is all taken from a book
980 if ("lineart".equals(type)) {
981 isFigure = true;
982 // media = Figure.NewInstance(url.toURI(), null, null, null);
983 } else if (type == null || "photo".equals(type)
984 || "signature".equals(type)
985 || "others".equals(type)) {
986 } else {
987 String message = "Unknown figure type '%s'";
988 message = String.format(message, type);
989 fireWarningEvent(message, next, 2);
990 }
991 media = docImport.getImageMedia(urlString, docImport.getReadMediaData(), isFigure);
992
993 if (media != null){
994 // title
995 if (StringUtils.isNotBlank(titleString)) {
996 media.putTitle(Language.DEFAULT(), titleString);
997 }
998 // legend
999 if (StringUtils.isNotBlank(legendString)) {
1000 media.addDescription(legendString, Language.DEFAULT());
1001 }
1002 if (StringUtils.isNotBlank(numString)) {
1003 // TODO use concrete source (e.g. DAPHNIPHYLLACEAE in FM
1004 // vol.13)
1005 Reference<?> citation = state.getConfig().getSourceReference();
1006 media.addSource(numString, "num", citation, null);
1007 // TODO name used in source if available
1008 }
1009 // TODO which citation
1010 if (StringUtils.isNotBlank(id)) {
1011 media.addSource(id, null, state.getConfig().getSourceReference(), null);
1012 } else {
1013 String message = "Figure id should never be empty or null";
1014 fireWarningEvent(message, next, 6);
1015 }
1016
1017 // text
1018 // do nothing
1019
1020 }
1021 } catch (MalformedURLException e) {
1022 String message = "Media uri has incorrect syntax: %s";
1023 message = String.format(message, urlString);
1024 fireWarningEvent(message, next, 4);
1025 // } catch (URISyntaxException e) {
1026 // String message = "Media uri has incorrect syntax: %s";
1027 // message = String.format(message, urlString);
1028 // fireWarningEvent(message, next, 4);
1029 }
1030
1031 registerGivenFigure(state, id, media);
1032 }
1033
1034 private FigureDataHolder handleFigureRef(MarkupImportState state,
1035 XMLEventReader reader, XMLEvent parentEvent)
1036 throws XMLStreamException {
1037 FigureDataHolder result = new FigureDataHolder();
1038 Map<String, Attribute> attributes = getAttributes(parentEvent);
1039 result.ref = getAndRemoveAttributeValue(attributes, REF);
1040 checkNoAttributes(attributes, parentEvent);
1041
1042 // text is not handled, needed only for debugging purposes
1043 String text = "";
1044 while (reader.hasNext()) {
1045 XMLEvent next = readNoWhitespace(reader);
1046 if (isMyEndingElement(next, parentEvent)) {
1047 return result;
1048 } else if (isStartingElement(next, NUM)) {
1049 String num = getCData(state, reader, next);
1050 result.num = num; // num is not handled during import
1051 } else if (isStartingElement(next, FIGURE_PART)) {
1052 result.figurePart = getCData(state, reader, next);
1053 } else if (next.isCharacters()) {
1054 text += next.asCharacters().getData();
1055 } else {
1056 fireUnexpectedEvent(next, 0);
1057 }
1058 }
1059 throw new IllegalStateException("<figureRef> has no end tag");
1060 }
1061
1062 private FootnoteDataHolder handleFootnote(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
1063 FootnoteDataHolder result = new FootnoteDataHolder();
1064 Map<String, Attribute> attributes = getAttributes(parentEvent);
1065 result.id = getAndRemoveAttributeValue(attributes, ID);
1066 // result.ref = getAndRemoveAttributeValue(attributes, REF);
1067 checkNoAttributes(attributes, parentEvent);
1068
1069 while (reader.hasNext()) {
1070 XMLEvent next = readNoWhitespace(reader);
1071 if (isStartingElement(next, FOOTNOTE_STRING)) {
1072 String string = handleFootnoteString(state, reader, next);
1073 result.string = string;
1074 } else if (isMyEndingElement(next, parentEvent)) {
1075 return result;
1076 } else {
1077 fireUnexpectedEvent(next, 0);
1078 }
1079 }
1080 return result;
1081 }
1082
1083 private FootnoteDataHolder handleFootnoteRef(MarkupImportState state,
1084 XMLEventReader reader, XMLEvent parentEvent)
1085 throws XMLStreamException {
1086 FootnoteDataHolder result = new FootnoteDataHolder();
1087 Map<String, Attribute> attributes = getAttributes(parentEvent);
1088 result.ref = getAndRemoveAttributeValue(attributes, REF);
1089 checkNoAttributes(attributes, parentEvent);
1090
1091 // text is not handled, needed only for debugging purposes
1092 String text = "";
1093 while (reader.hasNext()) {
1094 XMLEvent next = readNoWhitespace(reader);
1095 // if (isStartingElement(next, FOOTNOTE_STRING)){
1096 // String string = handleFootnoteString(state, reader, next);
1097 // result.string = string;
1098 // }else
1099 if (isMyEndingElement(next, parentEvent)) {
1100 return result;
1101 } else if (next.isCharacters()) {
1102 text += next.asCharacters().getData();
1103
1104 } else {
1105 fireUnexpectedEvent(next, 0);
1106 }
1107 }
1108 return result;
1109 }
1110
1111 private void handleNomenclature(MarkupImportState state,
1112 XMLEventReader reader, XMLEvent parentEvent)
1113 throws XMLStreamException {
1114 checkNoAttributes(parentEvent);
1115
1116 while (reader.hasNext()) {
1117 XMLEvent next = readNoWhitespace(reader);
1118 if (isStartingElement(next, HOMOTYPES)) {
1119 handleHomotypes(state, reader, next.asStartElement());
1120 } else if (isMyEndingElement(next, parentEvent)) {
1121 return;
1122 } else {
1123 fireSchemaConflictEventExpectedStartTag(HOMOTYPES, reader);
1124 state.setUnsuccessfull();
1125 }
1126 }
1127 return;
1128 }
1129
1130 private String handleFootnoteString(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
1131 boolean isTextMode = true;
1132 String text = "";
1133 while (reader.hasNext()) {
1134 XMLEvent next = readNoWhitespace(reader);
1135 if (isMyEndingElement(next, parentEvent)) {
1136 return text;
1137 } else if (next.isEndElement()) {
1138 if (isEndingElement(next, FULL_NAME)) {
1139 popUnimplemented(next.asEndElement());
1140 } else if (isEndingElement(next, BR)) {
1141 isTextMode = true;
1142 } else if (isHtml(next)) {
1143 text += getXmlTag(next);
1144 } else {
1145 handleUnexpectedEndElement(next.asEndElement());
1146 }
1147 } else if (next.isStartElement()) {
1148 if (isStartingElement(next, FULL_NAME)) {
1149 handleNotYetImplementedElement(next);
1150 } else if (isStartingElement(next, GATHERING)) {
1151 text += handleInLineGathering(state, reader, next);
1152 } else if (isStartingElement(next, REFERENCES)) {
1153 text += " " + handleInLineReferences(state, reader, next)+ " ";
1154 } else if (isStartingElement(next, BR)) {
1155 text += "<br/>";
1156 isTextMode = false;
1157 } else if (isHtml(next)) {
1158 text += getXmlTag(next);
1159 } else {
1160 handleUnexpectedStartElement(next.asStartElement());
1161 }
1162 } else if (next.isCharacters()) {
1163 if (!isTextMode) {
1164 String message = "footnoteString is not in text mode";
1165 fireWarningEvent(message, next, 6);
1166 } else {
1167 text += next.asCharacters().getData().trim();
1168 // getCData(state, reader, next); does not work as we have inner tags like <references>
1169 }
1170 } else {
1171 handleUnexpectedEndElement(next.asEndElement());
1172 }
1173 }
1174 throw new IllegalStateException("<footnoteString> has no closing tag");
1175
1176 }
1177
1178 private String handleInLineGathering(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
1179 DerivedUnitFacade facade = DerivedUnitFacade.NewInstance(DerivedUnitType.DerivedUnit.FieldObservation);
1180 handleGathering(state, reader, parentEvent, facade);
1181 FieldObservation fieldObservation = facade.innerFieldObservation();
1182 String result = "<cdm:specimen uuid='%s'>%s</specimen>";
1183 result = String.format(result, fieldObservation.getUuid(), fieldObservation.getTitleCache());
1184 save(fieldObservation, state);
1185 return result;
1186 }
1187
1188 private String handleInLineReferences(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
1189 checkNoAttributes(parentEvent);
1190
1191 boolean hasReference = false;
1192 String text = "";
1193 while (reader.hasNext()) {
1194 XMLEvent next = readNoWhitespace(reader);
1195 if (isMyEndingElement(next, parentEvent)) {
1196 checkMandatoryElement(hasReference, parentEvent.asStartElement(), REFERENCE);
1197 return text;
1198 } else if (isStartingElement(next, REFERENCE)) {
1199 text += handleInLineReference(state, reader, next);
1200 hasReference = true;
1201 } else {
1202 handleUnexpectedElement(next);
1203 }
1204 }
1205 throw new IllegalStateException("<References> has no closing tag");
1206 }
1207
1208 private String handleInLineReference(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent)throws XMLStreamException {
1209 Reference<?> reference = handleReference(state, reader, parentEvent);
1210 String result = "<cdm:ref uuid='%s'>%s</ref>";
1211 result = String.format(result, reference.getUuid(), reference.getTitleCache());
1212 save(reference, state);
1213 return result;
1214 }
1215
1216 private Reference<?> handleReference(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent)throws XMLStreamException {
1217 checkNoAttributes(parentEvent);
1218
1219 boolean hasRefPart = false;
1220 Map<String, String> refMap = new HashMap<String, String>();
1221 while (reader.hasNext()) {
1222 XMLEvent next = readNoWhitespace(reader);
1223 if (isMyEndingElement(next, parentEvent)) {
1224 checkMandatoryElement(hasRefPart, parentEvent.asStartElement(),
1225 REF_PART);
1226 Reference<?> reference = createReference(state, refMap, next);
1227 return reference;
1228 } else if (isStartingElement(next, REF_PART)) {
1229 handleRefPart(state, reader, next, refMap);
1230 hasRefPart = true;
1231 } else {
1232 handleUnexpectedElement(next);
1233 }
1234 }
1235 // TODO handle missing end element
1236 throw new IllegalStateException("<Reference> has no closing tag");
1237 }
1238
1239 private void handleHomotypes(MarkupImportState state, XMLEventReader reader, StartElement parentEvent)
1240 throws XMLStreamException {
1241 checkNoAttributes(parentEvent);
1242
1243 HomotypicalGroup homotypicalGroup = null;
1244
1245 boolean hasNom = false;
1246 while (reader.hasNext()) {
1247 XMLEvent next = readNoWhitespace(reader);
1248 if (next.isEndElement()) {
1249 if (isMyEndingElement(next, parentEvent)) {
1250 checkMandatoryElement(hasNom, parentEvent, NOM);
1251 return;
1252 } else {
1253 if (isEndingElement(next, NAME_TYPE)) {
1254 state.setNameType(false);
1255 } else if (isEndingElement(next, NOTES)) {
1256 // NOT YET IMPLEMENTED
1257 popUnimplemented(next.asEndElement());
1258 } else {
1259 handleUnexpectedEndElement(next.asEndElement());
1260 }
1261 }
1262 } else if (next.isStartElement()) {
1263 if (isStartingElement(next, NOM)) {
1264 NonViralName<?> name = handleNom(state, reader, next, homotypicalGroup);
1265 homotypicalGroup = name.getHomotypicalGroup();
1266 hasNom = true;
1267 } else if (isStartingElement(next, NAME_TYPE)) {
1268 state.setNameType(true);
1269 handleNameType(state, reader, next, homotypicalGroup);
1270 } else if (isStartingElement(next, SPECIMEN_TYPE)) {
1271 handleSpecimenType(state, reader, next, homotypicalGroup);
1272 } else if (isStartingElement(next, NOTES)) {
1273 handleNotYetImplementedElement(next);
1274 } else {
1275 handleUnexpectedStartElement(next);
1276 }
1277 } else {
1278 handleUnexpectedElement(next);
1279 }
1280 }
1281 // TODO handle missing end element
1282 throw new IllegalStateException("Homotypes has no closing tag");
1283
1284 }
1285
1286 private void handleNameType(MarkupImportState state, XMLEventReader reader,
1287 XMLEvent parentEvent, HomotypicalGroup homotypicalGroup)
1288 throws XMLStreamException {
1289 Map<String, Attribute> attributes = getAttributes(parentEvent);
1290 String typeStatus = getAndRemoveAttributeValue(attributes, TYPE_STATUS);
1291 checkNoAttributes(attributes, parentEvent);
1292
1293 NameTypeDesignationStatus status;
1294 try {
1295 status = NameTypeParser.parseNameTypeStatus(typeStatus);
1296 } catch (UnknownCdmTypeException e) {
1297 String message = "Type status could not be recognized: %s";
1298 message = String.format(message, typeStatus);
1299 fireWarningEvent(message, parentEvent, 4);
1300 status = null;
1301 }
1302
1303 boolean hasNom = false;
1304 while (reader.hasNext()) {
1305 XMLEvent next = readNoWhitespace(reader);
1306 if (next.isEndElement()) {
1307 if (isMyEndingElement(next, parentEvent)) {
1308 checkMandatoryElement(hasNom, parentEvent.asStartElement(),
1309 NOM);
1310 state.setNameType(false);
1311 return;
1312 } else {
1313 if (isEndingElement(next, ACCEPTED_NAME)) {
1314 // NOT YET IMPLEMENTED
1315 popUnimplemented(next.asEndElement());
1316 } else {
1317 handleUnexpectedEndElement(next.asEndElement());
1318 }
1319 }
1320 } else if (next.isStartElement()) {
1321 if (isStartingElement(next, NOM)) {
1322 // TODO should we check if the type is always a species, is
1323 // this a rule?
1324 NonViralName<?> speciesName = handleNom(state, reader,
1325 next, null);
1326 for (TaxonNameBase<?, ?> name : homotypicalGroup
1327 .getTypifiedNames()) {
1328 name.addNameTypeDesignation(speciesName, null, null,
1329 null, status, false, false, false, false);
1330 }
1331 hasNom = true;
1332 } else if (isStartingElement(next, ACCEPTED_NAME)) {
1333 handleNotYetImplementedElement(next);
1334 } else {
1335 handleUnexpectedStartElement(next);
1336 }
1337 } else {
1338 handleUnexpectedElement(next);
1339 }
1340 }
1341 // TODO handle missing end element
1342 throw new IllegalStateException("Homotypes has no closing tag");
1343
1344 }
1345
1346 private void handleSpecimenType(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent,
1347 HomotypicalGroup homotypicalGroup) throws XMLStreamException {
1348 // attributes
1349 Map<String, Attribute> attributes = getAttributes(parentEvent);
1350 String typeStatus = getAndRemoveAttributeValue(attributes, TYPE_STATUS);
1351 String notSeen = getAndRemoveAttributeValue(attributes, NOT_SEEN);
1352 String unknown = getAndRemoveAttributeValue(attributes, UNKNOWN);
1353 String notFound = getAndRemoveAttributeValue(attributes, NOT_FOUND);
1354 String destroyed = getAndRemoveAttributeValue(attributes, DESTROYED);
1355 String lost = getAndRemoveAttributeValue(attributes, LOST);
1356 checkNoAttributes(attributes, parentEvent);
1357 if (StringUtils.isNotEmpty(typeStatus)) {
1358 // TODO
1359 // currently not needed
1360 } else if (StringUtils.isNotEmpty(notSeen)) {
1361 handleNotYetImplementedAttribute(attributes, NOT_SEEN);
1362 } else if (StringUtils.isNotEmpty(unknown)) {
1363 handleNotYetImplementedAttribute(attributes, UNKNOWN);
1364 } else if (StringUtils.isNotEmpty(notFound)) {
1365 handleNotYetImplementedAttribute(attributes, NOT_FOUND);
1366 } else if (StringUtils.isNotEmpty(destroyed)) {
1367 handleNotYetImplementedAttribute(attributes, DESTROYED);
1368 } else if (StringUtils.isNotEmpty(lost)) {
1369 handleNotYetImplementedAttribute(attributes, LOST);
1370 }
1371
1372 NonViralName<?> firstName = null;
1373 Set<TaxonNameBase> names = homotypicalGroup.getTypifiedNames();
1374 if (names.isEmpty()) {
1375 String message = "There is no name in a homotypical group. Can't create the specimen type";
1376 fireWarningEvent(message, parentEvent, 8);
1377 } else {
1378 firstName = CdmBase.deproxy(names.iterator().next(),NonViralName.class);
1379 }
1380
1381 DerivedUnitFacade facade = DerivedUnitFacade.NewInstance(DerivedUnitType.Specimen);
1382 String text = "";
1383 // elements
1384 while (reader.hasNext()) {
1385 XMLEvent next = readNoWhitespace(reader);
1386 if (next.isEndElement()) {
1387 if (isMyEndingElement(next, parentEvent)) {
1388 makeSpecimenType(state, facade, text, firstName, parentEvent);
1389 return;
1390 } else {
1391 if (isEndingElement(next, FULL_TYPE)) {
1392 // NOT YET IMPLEMENTED
1393 popUnimplemented(next.asEndElement());
1394 } else if (isEndingElement(next, TYPE_STATUS)) {
1395 // NOT YET IMPLEMENTED
1396 popUnimplemented(next.asEndElement());
1397 } else if (isEndingElement(next, ORIGINAL_DETERMINATION)) {
1398 // NOT YET IMPLEMENTED
1399 popUnimplemented(next.asEndElement());
1400 } else if (isEndingElement(next, SPECIMEN_TYPE)) {
1401 // NOT YET IMPLEMENTED
1402 popUnimplemented(next.asEndElement());
1403 } else if (isEndingElement(next, COLLECTION_AND_TYPE)) {
1404 // NOT YET IMPLEMENTED
1405 popUnimplemented(next.asEndElement());
1406 } else if (isEndingElement(next, CITATION)) {
1407 // NOT YET IMPLEMENTED
1408 popUnimplemented(next.asEndElement());
1409 } else if (isEndingElement(next, NOTES)) {
1410 // NOT YET IMPLEMENTED
1411 popUnimplemented(next.asEndElement());
1412 } else if (isEndingElement(next, ANNOTATION)) {
1413 // NOT YET IMPLEMENTED
1414 popUnimplemented(next.asEndElement());
1415 } else {
1416 handleUnexpectedEndElement(next.asEndElement());
1417 }
1418 }
1419 } else if (next.isStartElement()) {
1420 if (isStartingElement(next, FULL_TYPE)) {
1421 handleNotYetImplementedElement(next);
1422 // homotypicalGroup = handleNom(state, reader, next, taxon,
1423 // homotypicalGroup);
1424 } else if (isStartingElement(next, TYPE_STATUS)) {
1425 handleNotYetImplementedElement(next);
1426 } else if (isStartingElement(next, GATHERING)) {
1427 handleGathering(state, reader, next, facade);
1428 } else if (isStartingElement(next, ORIGINAL_DETERMINATION)) {
1429 handleNotYetImplementedElement(next);
1430 } else if (isStartingElement(next, SPECIMEN_TYPE)) {
1431 handleNotYetImplementedElement(next);
1432 } else if (isStartingElement(next, COLLECTION_AND_TYPE)) {
1433 handleNotYetImplementedElement(next);
1434 } else if (isStartingElement(next, CITATION)) {
1435 handleNotYetImplementedElement(next);
1436 } else if (isStartingElement(next, NOTES)) {
1437 handleNotYetImplementedElement(next);
1438 } else if (isStartingElement(next, ANNOTATION)) {
1439 handleNotYetImplementedElement(next);
1440 } else {
1441 handleUnexpectedStartElement(next);
1442 }
1443 } else if (next.isCharacters()) {
1444 text += next.asCharacters().getData();
1445 } else {
1446 handleUnexpectedElement(next);
1447 }
1448 }
1449 // TODO handle missing end element
1450 throw new IllegalStateException("Specimen type has no closing tag");
1451 }
1452
1453 private void makeSpecimenType(MarkupImportState state, DerivedUnitFacade facade, String text,
1454 NonViralName name, XMLEvent parentEvent) {
1455 text = text.trim();
1456 // remove brackets
1457 if (text.matches("^\\(.*\\)\\.?$")) {
1458 text = text.replaceAll("\\.", "");
1459 text = text.substring(1, text.length() - 1);
1460 }
1461 String[] split = text.split("[;,]");
1462 for (String str : split) {
1463 str = str.trim();
1464 boolean addToAllNamesInGroup = true;
1465 TypeInfo typeInfo = makeSpecimenTypeTypeInfo(str, parentEvent);
1466 SpecimenTypeDesignationStatus typeStatus = typeInfo.status;
1467 Collection collection = createCollection(typeInfo.collectionString);
1468
1469 // TODO improve cache strategy handling
1470 DerivedUnitBase typeSpecimen = facade.addDuplicate(collection,
1471 null, null, null, null);
1472 typeSpecimen.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
1473 name.addSpecimenTypeDesignation((Specimen) typeSpecimen, typeStatus, null, null, null, false, addToAllNamesInGroup);
1474 }
1475 }
1476
1477 private Collection createCollection(String code) {
1478 // TODO deduplicate
1479 // TODO code <-> name
1480 Collection result = Collection.NewInstance();
1481 result.setCode(code);
1482 return result;
1483 }
1484
1485 private TypeInfo makeSpecimenTypeTypeInfo(String originalString, XMLEvent event) {
1486 TypeInfo result = new TypeInfo();
1487 String[] split = originalString.split("\\s+");
1488 for (String str : split) {
1489 if (str.matches(SpecimenTypeParser.typeTypePattern)) {
1490 SpecimenTypeDesignationStatus status;
1491 try {
1492 status = SpecimenTypeParser.parseSpecimenTypeStatus(str);
1493 } catch (UnknownCdmTypeException e) {
1494 String message = "Specimen type status '%s' not recognized by parser";
1495 message = String.format(message, str);
1496 fireWarningEvent(message, event, 4);
1497 status = null;
1498 }
1499 result.status = status;
1500 } else if (str.matches(SpecimenTypeParser.collectionPattern)) {
1501 result.collectionString = str;
1502 } else {
1503 String message = "Type part '%s' could not be recognized";
1504 message = String.format(message, str);
1505 fireWarningEvent(message, event, 2);
1506 }
1507 }
1508
1509 return result;
1510 }
1511
1512
1513 private void handleGathering(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent , DerivedUnitFacade facade) throws XMLStreamException {
1514 checkNoAttributes(parentEvent);
1515 boolean hasCollector = false;
1516 boolean hasFieldNum = false;
1517
1518 // elements
1519 while (reader.hasNext()) {
1520 XMLEvent next = readNoWhitespace(reader);
1521 if (next.isEndElement()) {
1522 if (isMyEndingElement(next, parentEvent)) {
1523 checkMandatoryElement(hasCollector,parentEvent.asStartElement(), COLLECTOR);
1524 checkMandatoryElement(hasFieldNum,parentEvent.asStartElement(), FIELD_NUM);
1525 return;
1526 } else {
1527 if (isEndingElement(next, ALTERNATIVE_COLLECTOR)) {
1528 // NOT YET IMPLEMENTED
1529 popUnimplemented(next.asEndElement());
1530 } else if (isEndingElement(next, ALTERNATIVE_FIELD_NUM)) {
1531 // NOT YET IMPLEMENTED
1532 popUnimplemented(next.asEndElement());
1533 } else if (isEndingElement(next, COLLECTION_TYPE_STATUS)) {
1534 // NOT YET IMPLEMENTED
1535 popUnimplemented(next.asEndElement());
1536 } else if (isEndingElement(next, COLLECTION_AND_TYPE)) {
1537 // NOT YET IMPLEMENTED , does this make sense here?
1538 popUnimplemented(next.asEndElement());
1539 } else if (isEndingElement(next,
1540 ALTERNATIVE_COLLECTION_TYPE_STATUS)) {
1541 // NOT YET IMPLEMENTED
1542 popUnimplemented(next.asEndElement());
1543 } else if (isEndingElement(next, SUB_COLLECTION)) {
1544 // NOT YET IMPLEMENTED
1545 popUnimplemented(next.asEndElement());
1546 } else if (isEndingElement(next, COLLECTION)) {
1547 // NOT YET IMPLEMENTED
1548 popUnimplemented(next.asEndElement());
1549 } else if (isEndingElement(next, DATES)) {
1550 // NOT YET IMPLEMENTED
1551 popUnimplemented(next.asEndElement());
1552 } else if (isEndingElement(next, NOTES)) {
1553 // NOT YET IMPLEMENTED
1554 popUnimplemented(next.asEndElement());
1555 } else {
1556 handleUnexpectedEndElement(next.asEndElement());
1557 }
1558 }
1559 } else if (next.isStartElement()) {
1560 if (isStartingElement(next, COLLECTOR)) {
1561 hasCollector = true;
1562 String collectorStr = getCData(state, reader, next);
1563 AgentBase<?> collector = createCollector(collectorStr);
1564 facade.setCollector(collector);
1565 } else if (isStartingElement(next, ALTERNATIVE_COLLECTOR)) {
1566 handleNotYetImplementedElement(next);
1567 } else if (isStartingElement(next, FIELD_NUM)) {
1568 hasFieldNum = true;
1569 String fieldNumStr = getCData(state, reader, next);
1570 facade.setFieldNumber(fieldNumStr);
1571 } else if (isStartingElement(next, ALTERNATIVE_FIELD_NUM)) {
1572 handleNotYetImplementedElement(next);
1573 } else if (isStartingElement(next, COLLECTION_TYPE_STATUS)) {
1574 handleNotYetImplementedElement(next);
1575 } else if (isStartingElement(next, COLLECTION_AND_TYPE)) { //does this make sense here?
1576 handleNotYetImplementedElement(next);
1577 } else if (isStartingElement(next, ALTERNATIVE_COLLECTION_TYPE_STATUS)) {
1578 handleNotYetImplementedElement(next);
1579 } else if (isStartingElement(next, SUB_COLLECTION)) {
1580 handleNotYetImplementedElement(next);
1581 } else if (isStartingElement(next, COLLECTION)) {
1582 handleNotYetImplementedElement(next);
1583 } else if (isStartingElement(next, LOCALITY)) {
1584 handleLocality(state, reader, next, facade);
1585 } else if (isStartingElement(next, DATES)) {
1586 handleNotYetImplementedElement(next);
1587 } else if (isStartingElement(next, NOTES)) {
1588 handleNotYetImplementedElement(next);
1589 } else {
1590 handleUnexpectedStartElement(next);
1591 }
1592 } else {
1593 handleUnexpectedElement(next);
1594 }
1595 }
1596 // TODO handle missing end element
1597 throw new IllegalStateException("Collection has no closing tag");
1598
1599 }
1600
1601 private void handleLocality(MarkupImportState state, XMLEventReader reader,XMLEvent parentEvent, DerivedUnitFacade facade)throws XMLStreamException {
1602 String classValue = getClassOnlyAttribute(parentEvent);
1603 boolean isLocality = false;
1604 NamedAreaLevel areaLevel = null;
1605 if ("locality".equalsIgnoreCase(classValue)) {
1606 isLocality = true;
1607 } else {
1608 areaLevel = makeNamedAreaLevel(state, classValue, parentEvent);
1609 }
1610
1611 String text = "";
1612 // elements
1613 while (reader.hasNext()) {
1614 XMLEvent next = readNoWhitespace(reader);
1615 if (next.isEndElement()) {
1616 if (isMyEndingElement(next, parentEvent)) {
1617 if (StringUtils.isNotBlank(text)) {
1618 text = normalize(text);
1619 if (isLocality) {
1620 facade.setLocality(text);
1621 } else {
1622 text = CdmUtils.removeTrailingDot(text);
1623 NamedArea area = makeArea(state, text, areaLevel);
1624 facade.addCollectingArea(area);
1625 }
1626 }
1627 // TODO
1628 return;
1629 } else {
1630 if (isEndingElement(next, ALTITUDE)) {
1631 // NOT YET IMPLEMENTED
1632 popUnimplemented(next.asEndElement());
1633 } else if (isEndingElement(next, COORDINATES)) {
1634 // NOT YET IMPLEMENTED
1635 popUnimplemented(next.asEndElement());
1636 } else if (isEndingElement(next, ANNOTATION)) {
1637 // NOT YET IMPLEMENTED
1638 popUnimplemented(next.asEndElement());
1639 } else {
1640 handleUnexpectedEndElement(next.asEndElement());
1641 }
1642 }
1643 } else if (next.isStartElement()) {
1644 if (isStartingElement(next, ALTITUDE)) {
1645 handleNotYetImplementedElement(next);
1646 // homotypicalGroup = handleNom(state, reader, next, taxon,
1647 // homotypicalGroup);
1648 } else if (isStartingElement(next, COORDINATES)) {
1649 handleNotYetImplementedElement(next);
1650 } else if (isStartingElement(next, ANNOTATION)) {
1651 handleNotYetImplementedElement(next);
1652 } else {
1653 handleUnexpectedStartElement(next);
1654 }
1655 } else if (next.isCharacters()) {
1656 text += next.asCharacters().getData();
1657 } else {
1658 handleUnexpectedElement(next);
1659 }
1660 }
1661 throw new IllegalStateException("<SpecimenType> has no closing tag");
1662 }
1663
1664 // private NamedArea createArea(String text, NamedAreaLevel areaLevel, MarkupImportState state) {
1665 // NamedArea area = NamedArea.NewInstance(text, text, null);
1666 // area.setLevel(areaLevel);
1667 // save(area, state);
1668 // return area;
1669 // }
1670
1671 private AgentBase<?> createCollector(String collectorStr) {
1672 return createAuthor(collectorStr);
1673 }
1674
1675 /**
1676 * Creates the name defined by a nom tag. Adds it to the given homotypical
1677 * group (if not null).
1678 *
1679 * @param state
1680 * @param reader
1681 * @param parentEvent
1682 * @param homotypicalGroup
1683 * @return
1684 * @throws XMLStreamException
1685 */
1686 private NonViralName<?> handleNom(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent,
1687 HomotypicalGroup homotypicalGroup) throws XMLStreamException {
1688 boolean isSynonym = false;
1689 boolean isNameType = state.isNameType();
1690 // attributes
1691 String classValue = getClassOnlyAttribute(parentEvent);
1692 NonViralName<?> name;
1693 if (!isNameType && ACCEPTED.equalsIgnoreCase(classValue)) {
1694 isSynonym = false;
1695 name = createName(state, homotypicalGroup, isSynonym);
1696 } else if (!isNameType && SYNONYM.equalsIgnoreCase(classValue)) {
1697 isSynonym = true;
1698 name = createName(state, homotypicalGroup, isSynonym);
1699 } else if (isNameType && NAME_TYPE.equalsIgnoreCase(classValue)) {
1700 // TODO do we need to define the rank here?
1701 name = createNameByCode(state, null);
1702 } else {
1703 fireUnexpectedAttributeValue(parentEvent, CLASS, classValue);
1704 name = createNameByCode(state, null);
1705 }
1706
1707 Map<String, String> nameMap = new HashMap<String, String>();
1708
1709 while (reader.hasNext()) {
1710 XMLEvent next = readNoWhitespace(reader);
1711 if (next.isEndElement()) {
1712 if (isMyEndingElement(next, parentEvent)) {
1713 // fill the name with all data gathered
1714 fillName(state, nameMap, name, next);
1715 return name;
1716 } else {
1717 if (isEndingElement(next, FULL_NAME)) {
1718 // NOT YET IMPLEMENTED
1719 popUnimplemented(next.asEndElement());
1720 } else if (isEndingElement(next, HOMONYM)) {
1721 // NOT YET IMPLEMENTED
1722 popUnimplemented(next.asEndElement());
1723 } else if (isEndingElement(next, NOTES)) {
1724 // NOT YET IMPLEMENTED
1725 popUnimplemented(next.asEndElement());
1726 } else if (isEndingElement(next, ANNOTATION)) {
1727 // NOT YET IMPLEMENTED
1728 popUnimplemented(next.asEndElement());
1729 } else {
1730 handleUnexpectedEndElement(next.asEndElement());
1731 }
1732 }
1733 } else if (next.isStartElement()) {
1734 if (isStartingElement(next, FULL_NAME)) {
1735 handleNotYetImplementedElement(next);
1736 // homotypicalGroup = handleNom(state, reader, next, taxon,
1737 // homotypicalGroup);
1738 } else if (isStartingElement(next, NUM)) {
1739 String num = getCData(state, reader, next);
1740 num = num.replace(".", "");
1741 num = num.replace(")", "");
1742 if (StringUtils.isNotBlank(num)){
1743 if (state.getCurrentTaxonNum() != null && ! state.getCurrentTaxonNum().equals(num) ){
1744 String message = "Taxontitle num and homotypes/nom/num differ ( %s <-> %s ). I use the later one.";
1745 message = String.format(message, state.getCurrentTaxonNum(), num);
1746 fireWarningEvent(message, next, 4);
1747 }
1748 state.setCurrentTaxonNum(num);
1749 }
1750 } else if (isStartingElement(next, NAME)) {
1751 handleName(state, reader, next, nameMap);
1752 } else if (isStartingElement(next, CITATION)) {
1753 handleCitation(state, reader, next, name);
1754 } else if (isStartingElement(next, HOMONYM)) {
1755 handleNotYetImplementedElement(next);
1756 } else if (isStartingElement(next, NOTES)) {
1757 handleNotYetImplementedElement(next);
1758 } else if (isStartingElement(next, ANNOTATION)) {
1759 handleNotYetImplementedElement(next);
1760 } else {
1761 handleUnexpectedStartElement(next);
1762 }
1763 } else {
1764 handleUnexpectedElement(next);
1765 }
1766 }
1767 // TODO handle missing end element
1768 throw new IllegalStateException("Nom has no closing tag");
1769
1770 }
1771
1772 private void fillName(MarkupImportState state, Map<String, String> nameMap,
1773 NonViralName name, XMLEvent event) {
1774
1775 // Ranks: family, subfamily, tribus, genus, subgenus, section,
1776 // subsection, species, subspecies, variety, subvariety, forma
1777 // infrank, paraut, author, infrparaut, infraut, status, notes
1778
1779 String infrank = getAndRemoveMapKey(nameMap, INFRANK);
1780 String authorStr = getAndRemoveMapKey(nameMap, AUTHOR);
1781 String paraut = getAndRemoveMapKey(nameMap, PARAUT);
1782
1783 String infrParAut = getAndRemoveMapKey(nameMap, INFRPARAUT);
1784 String infrAut = getAndRemoveMapKey(nameMap, INFRAUT);
1785
1786 String statusStr = getAndRemoveMapKey(nameMap, STATUS);
1787 String notes = getAndRemoveMapKey(nameMap, NOTES);
1788
1789 makeRankDecision(state, nameMap, name, event, infrank);
1790
1791 // test consistency of rank and authors
1792 testRankAuthorConsistency(name, event, authorStr, paraut, infrParAut,infrAut);
1793
1794 // authors
1795 makeNomenclaturalAuthors(name, event, authorStr, paraut, infrParAut,infrAut);
1796
1797 // status
1798 // TODO handle pro parte, pro syn. etc.
1799 if (StringUtils.isNotBlank(statusStr)) {
1800 String proPartePattern = "(pro parte|p.p.)";
1801 if (statusStr.matches(proPartePattern)) {
1802 state.setProParte(true);
1803 }
1804 try {
1805 // TODO handle trim earlier
1806 statusStr = statusStr.trim();
1807 NomenclaturalStatusType nomStatusType = NomenclaturalStatusType.getNomenclaturalStatusTypeByAbbreviation(statusStr);
1808 name.addStatus(NomenclaturalStatus.NewInstance(nomStatusType));
1809 } catch (UnknownCdmTypeException e) {
1810 String message = "Status '%s' could not be recognized";
1811 message = String.format(message, statusStr);
1812 fireWarningEvent(message, event, 4);
1813 }
1814 }
1815
1816 // notes
1817 if (StringUtils.isNotBlank(notes)) {
1818 handleNotYetImplementedAttributeValue(event, CLASS, NOTES);
1819 }
1820
1821 return;
1822 }
1823
1824 /**
1825 * @param state
1826 * @param nameMap
1827 * @param name
1828 * @param event
1829 * @param infrankStr
1830 */
1831 private void makeRankDecision(MarkupImportState state,
1832 Map<String, String> nameMap, NonViralName<?> name, XMLEvent event,
1833 String infrankStr) {
1834 // TODO ranks
1835 for (String key : nameMap.keySet()) {
1836 Rank rank = makeRank(state, key, false);
1837 if (rank == null) {
1838 handleNotYetImplementedAttributeValue(event, CLASS, key);
1839 } else {
1840 if (name.getRank() == null || rank.isLower(name.getRank())) {
1841 name.setRank(rank);
1842 }
1843 String value = nameMap.get(key);
1844 if (rank.isSupraGeneric() || rank.isGenus()) {
1845 name.setGenusOrUninomial(toFirstCapital(value));
1846 } else if (rank.isInfraGeneric()) {
1847 name.setInfraGenericEpithet(toFirstCapital(value));
1848 } else if (rank.isSpecies()) {
1849 if (isFirstCapitalWord(value)){ //capital letters are allowed for species epithet in case of person names (e.g. Manilkara Welwitschii Engl.
1850 name.setSpecificEpithet(value);
1851 }else{
1852 name.setSpecificEpithet(value.toLowerCase());
1853 }
1854 } else if (rank.isInfraSpecific()) {
1855 name.setInfraSpecificEpithet(value.toLowerCase());
1856 } else {
1857 String message = "Invalid rank '%s'. Can't decide which epithet to fill with '%s'";
1858 message = String.format(message, rank.getTitleCache(),value);
1859 fireWarningEvent(message, event, 4);
1860 }
1861 }
1862
1863 }
1864 // handle given infrank marker
1865 if (StringUtils.isNotBlank(infrankStr)) {
1866 Rank infRank = makeRank(state, infrankStr, true);
1867
1868 if (infRank == null) {
1869 String message = "Infrank '%s' rank not recognized";
1870 message = String.format(message, infrankStr);
1871 fireWarningEvent(message, event, 4);
1872 } else {
1873 if (name.getRank() == null) {
1874 name.setRank(infRank);
1875 } else if (infRank.isLower(name.getRank())) {
1876 String message = "InfRank '%s' is lower than existing rank ";
1877 message = String.format(message, infrankStr);
1878 fireWarningEvent(message, event, 2);
1879 name.setRank(infRank);
1880 } else if (infRank.equals(name.getRank())) {
1881 // nothing
1882 } else {
1883 String message = "InfRank '%s' is higher than existing rank ";
1884 message = String.format(message, infrankStr);
1885 fireWarningEvent(message, event, 2);
1886 }
1887 }
1888 }
1889 }
1890
1891 private String toFirstCapital(String value) {
1892 if (StringUtils.isBlank(value)){
1893 return value;
1894 }else{
1895 String result = "";
1896 result += value.substring(0,1).toUpperCase();
1897 if (value.length()>1){
1898 result += value.substring(1).toLowerCase();
1899 }
1900 return result;
1901 }
1902 }
1903
1904 /**
1905 * @param name
1906 * @param event
1907 * @param authorStr
1908 * @param paraut
1909 * @param infrParAut
1910 * @param infrAut
1911 */
1912 private void makeNomenclaturalAuthors(NonViralName name, XMLEvent event,
1913 String authorStr, String paraut, String infrParAut, String infrAut) {
1914 if (name.getRank() != null && name.getRank().isInfraSpecific()) {
1915 if (StringUtils.isNotBlank(infrAut)) {
1916 INomenclaturalAuthor[] authorAndEx = authorAndEx(infrAut, event);
1917 name.setCombinationAuthorTeam(authorAndEx[0]);
1918 name.setExCombinationAuthorTeam(authorAndEx[1]);
1919 }
1920 if (StringUtils.isNotBlank(infrParAut)) {
1921 INomenclaturalAuthor[] authorAndEx = authorAndEx(infrParAut, event);
1922 name.setBasionymAuthorTeam(authorAndEx[0]);
1923 name.setExBasionymAuthorTeam(authorAndEx[1]);
1924 }
1925 } else {
1926 if (name.getRank() == null){
1927 String message = "No rank defined. Check correct usage of authors!";
1928 fireWarningEvent(message, event, 4);
1929 if (isNotBlank(infrParAut) || isNotBlank(infrAut)){
1930 authorStr = infrAut;
1931 paraut = infrParAut;
1932 }
1933 }
1934 if (StringUtils.isNotBlank(authorStr)) {
1935 INomenclaturalAuthor[] authorAndEx = authorAndEx(authorStr, event);
1936 name.setCombinationAuthorTeam(authorAndEx[0]);
1937 name.setExCombinationAuthorTeam(authorAndEx[1]);
1938 }
1939 if (StringUtils.isNotBlank(paraut)) {
1940 INomenclaturalAuthor[] authorAndEx = authorAndEx(paraut, event);
1941 name.setBasionymAuthorTeam(authorAndEx[0]);
1942 name.setExBasionymAuthorTeam(authorAndEx[1]);
1943 }
1944 }
1945 }
1946
1947 private TeamOrPersonBase[] authorAndEx(String authorAndEx, XMLEvent xmlEvent) {
1948 authorAndEx = authorAndEx.trim();
1949 TeamOrPersonBase[] result = new TeamOrPersonBase[2];
1950
1951 String[] split = authorAndEx.split("\\sex\\s");
1952 if (split.length > 2) {
1953 String message = "There is more then 1 ' ex ' in author string. Can't separate author and ex-author";
1954 fireWarningEvent(message, xmlEvent, 4);
1955 result[0] = createAuthor(authorAndEx);
1956 } else if (split.length == 2) {
1957 result[0] = createAuthor(split[1]);
1958 result[1] = createAuthor(split[0]);
1959 } else {
1960 result[0] = createAuthor(split[0]);
1961 }
1962 return result;
1963 }
1964
1965 /**
1966 * Tests if the names rank is consistent with the given author strings.
1967 * @param name
1968 * @param event
1969 * @param authorStr
1970 * @param paraut
1971 * @param infrParAut
1972 * @param infrAut
1973 */
1974 private void testRankAuthorConsistency(NonViralName name, XMLEvent event,
1975 String authorStr, String paraut, String infrParAut, String infrAut) {
1976 if (name.getRank() == null){
1977 return;
1978 }
1979 if (name.getRank().isInfraSpecific()) {
1980 if (StringUtils.isBlank(infrParAut)
1981 && StringUtils.isBlank(infrAut) //was isNotBlank before 29.5.2012
1982 && (StringUtils.isNotBlank(paraut) || StringUtils.isNotBlank(authorStr))
1983 && ! name.isAutonym()) {
1984 String message = "Rank is infraspecicific but has only specific or higher author(s)";
1985 fireWarningEvent(message, event, 4);
1986 }
1987 } else {
1988 // is not infraspecific
1989 if (StringUtils.isNotBlank(infrParAut) || StringUtils.isNotBlank(infrAut)) {
1990 String message = "Rank is not infraspecicific but name has infra author(s)";
1991 fireWarningEvent(message, event, 4);
1992 }
1993 }
1994 }
1995
1996 /**
1997 * Returns the (empty) name with the correct homotypical group depending on
1998 * the taxon status. Throws NPE if no currentTaxon is set in state.
1999 *
2000 * @param state
2001 * @param homotypicalGroup
2002 * @param isSynonym
2003 * @return
2004 */
2005 private NonViralName<?> createName(MarkupImportState state,
2006 HomotypicalGroup homotypicalGroup, boolean isSynonym) {
2007 NonViralName<?> name;
2008 Taxon taxon = state.getCurrentTaxon();
2009 if (isSynonym) {
2010 Rank defaultRank = Rank.SPECIES(); // can be any
2011 name = createNameByCode(state, defaultRank);
2012 if (homotypicalGroup != null) {
2013 name.setHomotypicalGroup(homotypicalGroup);
2014 }
2015 SynonymRelationshipType synonymType = SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF();
2016 if (taxon.getHomotypicGroup().equals(homotypicalGroup)) {
2017 synonymType = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
2018 }
2019 taxon.addSynonymName(name, synonymType);
2020 } else {
2021 name = CdmBase.deproxy(taxon.getName(), NonViralName.class);
2022 }
2023 return name;
2024 }
2025
2026 private void handleName(MarkupImportState state, XMLEventReader reader,
2027 XMLEvent parentEvent, Map<String, String> nameMap)
2028 throws XMLStreamException {
2029 String classValue = getClassOnlyAttribute(parentEvent);
2030
2031 String text = "";
2032 while (reader.hasNext()) {
2033 XMLEvent next = readNoWhitespace(reader);
2034 if (isMyEndingElement(next, parentEvent)) {
2035 nameMap.put(classValue, text);
2036 return;
2037 } else if (next.isStartElement()) {
2038 if (isStartingElement(next, ANNOTATION)) {
2039 handleNotYetImplementedElement(next);
2040 } else {
2041 handleUnexpectedStartElement(next.asStartElement());
2042 }
2043 } else if (next.isCharacters()) {
2044 text += next.asCharacters().getData();
2045 } else {
2046 handleUnexpectedEndElement(next.asEndElement());
2047 }
2048 }
2049 throw new IllegalStateException("name has no closing tag");
2050
2051 }
2052
2053 /**
2054 * @param state
2055 * @param classValue
2056 * @param byAbbrev
2057 * @return
2058 */
2059 private Rank makeRank(MarkupImportState state, String value,
2060 boolean byAbbrev) {
2061 Rank rank = null;
2062 if (StringUtils.isBlank(value)) {
2063 return null;
2064 }
2065 try {
2066 boolean useUnknown = true;
2067 NomenclaturalCode nc = makeNomenclaturalCode(state);
2068 if (byAbbrev) {
2069 rank = Rank.getRankByAbbreviation(value, nc, useUnknown);
2070 } else {
2071 rank = Rank.getRankByEnglishName(value, nc, useUnknown);
2072 }
2073 if (rank.equals(Rank.UNKNOWN_RANK())) {
2074 rank = null;
2075 }
2076 } catch (UnknownCdmTypeException e) {
2077 // doNothing
2078 }
2079 return rank;
2080 }
2081
2082 // public void handleNameNotRank(MarkupImportState state, XMLEventReader
2083 // reader, XMLEvent parentEvent, String classValue, NonViralName name)
2084 // throws XMLStreamException {
2085 // if (ACCEPTED.equalsIgnoreCase(classValue)){
2086 // }else if (SYNONYM.equalsIgnoreCase(classValue)){
2087 // }else{
2088 // //TODO Not yet implemented
2089 // handleNotYetImplementedAttributeValue(parentEvent, CLASS, classValue);
2090 // }
2091 // }
2092
2093 private void handleCitation(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, NonViralName name) throws XMLStreamException {
2094 String classValue = getClassOnlyAttribute(parentEvent);
2095
2096 state.setCitation(true);
2097 boolean hasRefPart = false;
2098 Map<String, String> refMap = new HashMap<String, String>();
2099 while (reader.hasNext()) {
2100 XMLEvent next = readNoWhitespace(reader);
2101 if (isMyEndingElement(next, parentEvent)) {
2102 checkMandatoryElement(hasRefPart, parentEvent.asStartElement(),
2103 REF_PART);
2104 Reference<?> reference = createReference(state, refMap, next);
2105 String microReference = refMap.get(DETAILS);
2106 doCitation(state, name, classValue, reference, microReference,
2107 parentEvent);
2108 state.setCitation(false);
2109 return;
2110 } else if (isStartingElement(next, REF_PART)) {
2111 handleRefPart(state, reader, next, refMap);
2112 hasRefPart = true;
2113 } else {
2114 handleUnexpectedElement(next);
2115 }
2116 }
2117 throw new IllegalStateException("Citation has no closing tag");
2118
2119 }
2120
2121 private void handleRefPart(MarkupImportState state, XMLEventReader reader,XMLEvent parentEvent, Map<String, String> refMap) throws XMLStreamException {
2122 String classValue = getClassOnlyAttribute(parentEvent);
2123
2124 String text = "";
2125 while (reader.hasNext()) {
2126 XMLEvent next = readNoWhitespace(reader);
2127 if (isMyEndingElement(next, parentEvent)) {
2128 refMap.put(classValue, text);
2129 return;
2130 } else if (next.isStartElement()) {
2131 if (isStartingElement(next, ANNOTATION)) {
2132 handleNotYetImplementedElement(next);
2133 } else if (isStartingElement(next, ITALICS)) {
2134 handleNotYetImplementedElement(next);
2135 } else if (isStartingElement(next, BOLD)) {
2136 handleNotYetImplementedElement(next);
2137 } else {
2138 handleUnexpectedStartElement(next.asStartElement());
2139 }
2140 } else if (next.isCharacters()) {
2141 text += next.asCharacters().getData();
2142 } else {
2143 handleUnexpectedEndElement(next.asEndElement());
2144 }
2145 }
2146 throw new IllegalStateException("RefPart has no closing tag");
2147
2148 }
2149
2150 private Reference<?> createReference(MarkupImportState state, Map<String, String> refMap, XMLEvent parentEvent) {
2151 // TODO
2152 Reference<?> reference;
2153
2154 String type = getAndRemoveMapKey(refMap, PUBTYPE);
2155 String authorStr = getAndRemoveMapKey(refMap, AUTHOR);
2156 String titleStr = getAndRemoveMapKey(refMap, PUBTITLE);
2157 String titleCache = getAndRemoveMapKey(refMap, PUBFULLNAME);
2158 String volume = getAndRemoveMapKey(refMap, VOLUME);
2159 String edition = getAndRemoveMapKey(refMap, EDITION);
2160 String editors = getAndRemoveMapKey(refMap, EDITORS);
2161 String year = getAndRemoveMapKey(refMap, YEAR);
2162 String pubName = getAndRemoveMapKey(refMap, PUBNAME);
2163 String pages = getAndRemoveMapKey(refMap, PAGES);
2164
2165 if (state.isCitation()) {
2166 if (volume != null || "journal".equalsIgnoreCase(type)) {
2167 IArticle article = ReferenceFactory.newArticle();
2168 if (pubName != null) {
2169 IJournal journal = ReferenceFactory.newJournal();
2170 journal.setTitle(pubName);
2171 article.setInJournal(journal);
2172 }
2173 reference = (Reference<?>) article;
2174
2175 } else {
2176 // TODO
2177 if (pubName != null){
2178 reference = ReferenceFactory.newBookSection();
2179 }else{
2180 reference = ReferenceFactory.newBook();
2181 }
2182 }
2183 // TODO use existing author from name or before
2184 TeamOrPersonBase<?> author = createAuthor(authorStr);
2185 reference.setAuthorTeam(author);
2186
2187 reference.setTitle(titleStr);
2188 if (StringUtils.isNotBlank(titleCache)) {
2189 reference.setTitleCache(titleCache, true);
2190 }
2191 reference.setEdition(edition);
2192 reference.setEditor(editors);
2193
2194 if (pubName != null) {
2195 Reference<?> inReference;
2196 if (reference.getType().equals(ReferenceType.Article)) {
2197 inReference = ReferenceFactory.newJournal();
2198 } else {
2199 inReference = ReferenceFactory.newGeneric();
2200 }
2201 inReference.setTitle(pubName);
2202 reference.setInReference(inReference);
2203 }
2204
2205
2206 } else { //no citation
2207 if (volume != null || "journal".equalsIgnoreCase(type)) {
2208 IArticle article = ReferenceFactory.newArticle();
2209 if (pubName != null) {
2210 IJournal journal = ReferenceFactory.newJournal();
2211 journal.setTitle(pubName);
2212 article.setInJournal(journal);
2213 }
2214 reference = (Reference<?>) article;
2215
2216 } else {
2217 Reference<?> bookOrPartOf = ReferenceFactory.newGeneric();
2218 reference = bookOrPartOf;
2219 }
2220
2221 // TODO type
2222 TeamOrPersonBase<?> author = createAuthor(authorStr);
2223 reference.setAuthorTeam(author);
2224
2225 reference.setTitle(titleStr);
2226 if (StringUtils.isNotBlank(titleCache)) {
2227 reference.setTitleCache(titleCache, true);
2228 }
2229 reference.setEdition(edition);
2230 reference.setEditor(editors);
2231
2232 if (pubName != null) {
2233 Reference<?> inReference;
2234 if (reference.getType().equals(ReferenceType.Article)) {
2235 inReference = ReferenceFactory.newJournal();
2236 } else {
2237 inReference = ReferenceFactory.newGeneric();
2238 }
2239 inReference.setTitle(pubName);
2240 reference.setInReference(inReference);
2241 }
2242 }
2243 reference.setVolume(volume);
2244 reference.setDatePublished(TimePeriod.parseString(year));
2245 //TODO check if this is handled correctly in FM markup
2246 reference.setPages(pages);
2247
2248 // TODO
2249 String[] unhandledList = new String[]{ALTERNATEPUBTITLE, ISSUE, NOTES, STATUS};
2250 for (String unhandled : unhandledList){
2251 String value = getAndRemoveMapKey(refMap, unhandled);
2252 if (isNotBlank(value)){
2253 this.handleNotYetImplementedAttributeValue(parentEvent, CLASS, unhandled);
2254 }
2255 }
2256
2257 for (String key : refMap.keySet()) {
2258 if (!DETAILS.equalsIgnoreCase(key)) {
2259 this.fireUnexpectedAttributeValue(parentEvent, CLASS, key);
2260 }
2261 }
2262
2263 return reference;
2264 }
2265
2266 private TeamOrPersonBase createAuthor(String authorTitle) {
2267 // TODO atomize and also use by name creation
2268 TeamOrPersonBase result = Team.NewTitledInstance(authorTitle,
2269 authorTitle);
2270 return result;
2271 }
2272
2273 private String getAndRemoveMapKey(Map<String, String> map, String key) {
2274 String result = map.get(key);
2275 map.remove(key);
2276 if (result != null) {
2277 result = normalize(result);
2278 }
2279 return StringUtils.stripToNull(result);
2280 }
2281
2282 private void doCitation(MarkupImportState state, NonViralName name,
2283 String classValue, Reference reference, String microCitation,
2284 XMLEvent parentEvent) {
2285 if (PUBLICATION.equalsIgnoreCase(classValue)) {
2286 name.setNomenclaturalReference(reference);
2287 name.setNomenclaturalMicroReference(microCitation);
2288 } else if (USAGE.equalsIgnoreCase(classValue)) {
2289 Taxon taxon = state.getCurrentTaxon();
2290 TaxonDescription td = getTaxonDescription(taxon, state
2291 .getConfig().getSourceReference(), false, true);
2292 TextData citation = TextData.NewInstance(Feature.CITATION());
2293 // TODO name used in source
2294 citation.addSource(null, null, reference, microCitation);
2295 td.addElement(citation);
2296 } else if (TYPE.equalsIgnoreCase(classValue)) {
2297 handleNotYetImplementedAttributeValue(parentEvent, CLASS,
2298 classValue);
2299 } else {
2300 // TODO Not yet implemented
2301 handleNotYetImplementedAttributeValue(parentEvent, CLASS,
2302 classValue);
2303 }
2304 }
2305
2306 private void handleFeature(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
2307 String classValue = getClassOnlyAttribute(parentEvent);
2308 Feature feature = makeFeature(classValue, state, parentEvent);
2309 Taxon taxon = state.getCurrentTaxon();
2310 TaxonDescription taxonDescription = getTaxonDescription(taxon, state.getConfig().getSourceReference(), NO_IMAGE_GALLERY, CREATE_NEW);
2311 // TextData figureHolderTextData = null; //for use with one TextData for
2312 // all figure only
2313
2314 boolean isDescription = feature.equals(Feature.DESCRIPTION());
2315 DescriptionElementBase lastDescriptionElement = null;
2316
2317 while (reader.hasNext()) {
2318 XMLEvent next = readNoWhitespace(reader);
2319 if (isMyEndingElement(next, parentEvent)) {
2320 return;
2321 } else if (isEndingElement(next, DISTRIBUTION_LIST) || isEndingElement(next, HABITAT_LIST)) {
2322 // only handle list elements
2323 } else if (isStartingElement(next, HEADING)) {
2324 makeFeatureHeading(state, reader, classValue, feature, next);
2325 } else if (isStartingElement(next, WRITER)) {
2326 makeFeatureWriter(state, reader, feature, taxon, next);
2327 // } else if (isStartingElement(next, DISTRIBUTION_LOCALITY)) {
2328 // if (!feature.equals(Feature.DISTRIBUTION())) {
2329 // String message = "Distribution locality only allowed for feature of type 'distribution'";
2330 // fireWarningEvent(message, next, 4);
2331 // }
2332 // handleDistributionLocality(state, reader, next);
2333 } else if (isStartingElement(next, DISTRIBUTION_LIST) || isStartingElement(next, HABITAT_LIST)) {
2334 // only handle single list elements
2335 } else if (isStartingElement(next, HABITAT)) {
2336 if (!(feature.equals(Feature.HABITAT())
2337 || feature.equals(Feature.HABITAT_ECOLOGY())
2338 || feature.equals(Feature.ECOLOGY()))) {
2339 String message = "Habitat only allowed for feature of type 'habitat','habitat ecology' or 'ecology'";
2340 fireWarningEvent(message, next, 4);
2341 }
2342 handleHabitat(state, reader, next);
2343 } else if (isStartingElement(next, CHAR)) {
2344 TextData textData = handleChar(state, reader, next);
2345 taxonDescription.addElement(textData);
2346 } else if (isStartingElement(next, STRING)) {
2347 lastDescriptionElement = makeFeatureString(state, reader,feature, taxonDescription, lastDescriptionElement,next);
2348 } else if (isStartingElement(next, FIGURE_REF)) {
2349 lastDescriptionElement = makeFeatureFigureRef(state, reader, taxonDescription, isDescription, lastDescriptionElement, next);
2350 } else if (isStartingElement(next, REFERENCES)) {
2351 // TODO details/microcitation ??
2352
2353 List<Reference<?>> refs = handleReferences(state, reader, next);
2354 if (!refs.isEmpty()) {
2355 // TODO
2356 Reference<?> descriptionRef = state.getConfig().getSourceReference();
2357 TaxonDescription description = getTaxonDescription(taxon, descriptionRef, false, true);
2358 TextData featurePlaceholder = docImport.getFeaturePlaceholder(state, description, feature, true);
2359 for (Reference<?> citation : refs) {
2360 featurePlaceholder.addSource(null, null, citation, null);
2361 }
2362 } else {
2363 String message = "No reference found in references";
2364 fireWarningEvent(message, next, 6);
2365 }
2366 } else if (isStartingElement(next, NUM)) {
2367 //TODO
2368 handleNotYetImplementedElement(next);
2369 } else if (isEndingElement(next, NUM)) {
2370 //TODO
2371 popUnimplemented(next.asEndElement());
2372 } else {
2373 handleUnexpectedElement(next);
2374 }
2375 }
2376 throw new IllegalStateException("<Feature> has no closing tag");
2377 }
2378
2379 /**
2380 * @param state
2381 * @param reader
2382 * @param taxonDescription
2383 * @param isDescription
2384 * @param lastDescriptionElement
2385 * @param next
2386 * @return
2387 * @throws XMLStreamException
2388 */
2389 private DescriptionElementBase makeFeatureFigureRef(MarkupImportState state, XMLEventReader reader,TaxonDescription taxonDescription,
2390 boolean isDescription, DescriptionElementBase lastDescriptionElement, XMLEvent next)throws XMLStreamException {
2391 FigureDataHolder figureHolder = handleFigureRef(state, reader, next);
2392 Feature figureFeature = getFeature(state,MarkupTransformer.uuidFigures, "Figures", "Figures", "Fig.",null);
2393 if (isDescription) {
2394 TextData figureHolderTextData = null;
2395 // if (figureHolderTextData == null){
2396 figureHolderTextData = TextData.NewInstance(figureFeature);
2397 if (StringUtils.isNotBlank(figureHolder.num)) {
2398 String annotationText = "<num>" + figureHolder.num.trim()
2399 + "</num>";
2400 Annotation annotation = Annotation.NewInstance(annotationText,
2401 AnnotationType.TECHNICAL(), Language.DEFAULT());
2402 figureHolderTextData.addAnnotation(annotation);
2403 }
2404 if (StringUtils.isNotBlank(figureHolder.figurePart)) {
2405 String annotationText = "<figurePart>"+ figureHolder.figurePart.trim() + "</figurePart>";
2406 Annotation annotation = Annotation.NewInstance(annotationText,AnnotationType.EDITORIAL(), Language.DEFAULT());
2407 figureHolderTextData.addAnnotation(annotation);
2408 }
2409 // if (StringUtils.isNotBlank(figureText)){
2410 // figureHolderTextData.putText(Language.DEFAULT(), figureText);
2411 // }
2412 taxonDescription.addElement(figureHolderTextData);
2413 // }
2414 registerFigureDemand(state, figureHolderTextData, figureHolder.ref);
2415 } else {
2416 if (lastDescriptionElement == null) {
2417 String message = "No description element created yet that can be referred by figure. Create new TextData instead";
2418 fireWarningEvent(message, next, 4);
2419 lastDescriptionElement = TextData.NewInstance(figureFeature);
2420 taxonDescription.addElement(lastDescriptionElement);
2421 }
2422 registerFigureDemand(state, lastDescriptionElement,
2423 figureHolder.ref);
2424 }
2425 return lastDescriptionElement;
2426 }
2427
2428 /**
2429 * @param state
2430 * @param reader
2431 * @param feature
2432 * @param taxonDescription
2433 * @param lastDescriptionElement
2434 * @param distributionList
2435 * @param next
2436 * @return
2437 * @throws XMLStreamException
2438 */
2439 private DescriptionElementBase makeFeatureString(MarkupImportState state,XMLEventReader reader, Feature feature,
2440 TaxonDescription taxonDescription, DescriptionElementBase lastDescriptionElement, XMLEvent next) throws XMLStreamException {
2441 //for specimen only
2442 if (feature.equals(Feature.SPECIMEN()) || feature.equals(Feature.MATERIALS_EXAMINED())){
2443
2444 List<DescriptionElementBase> specimens = handleMaterialsExamined(state, reader, next);
2445 for (DescriptionElementBase specimen : specimens){
2446 taxonDescription.addElement(specimen);
2447 lastDescriptionElement = specimen;
2448 }
2449 return lastDescriptionElement;
2450 }
2451
2452 //others
2453 Map<String, String> subheadingMap = handleString(state, reader, next, feature);
2454 for (String subheading : subheadingMap.keySet()) {
2455 Feature subheadingFeature = feature;
2456 if (StringUtils.isNotBlank(subheading) && subheadingMap.size() > 1) {
2457 subheadingFeature = makeFeature(subheading, state, next);
2458 }
2459 if (feature.equals(Feature.COMMON_NAME())){
2460 List<DescriptionElementBase> commonNames = makeVernacular(state, subheading, subheadingMap.get(subheading));
2461 for (DescriptionElementBase commonName : commonNames){
2462 taxonDescription.addElement(commonName);
2463 lastDescriptionElement = commonName;
2464 }
2465 }else {
2466 TextData textData = TextData.NewInstance(subheadingFeature);
2467 textData.putText(Language.DEFAULT(), subheadingMap.get(subheading));
2468 taxonDescription.addElement(textData);
2469 lastDescriptionElement = textData;
2470 // TODO how to handle figures when these data are split in
2471 // subheadings
2472 }
2473 }
2474 return lastDescriptionElement;
2475 }
2476
2477 private List<DescriptionElementBase> makeVernacular(MarkupImportState state, String subheading, String commonNameString) throws XMLStreamException {
2478 List<DescriptionElementBase> result = new ArrayList<DescriptionElementBase>();
2479 String[] splits = commonNameString.split(",");
2480 for (String split : splits){
2481 split = split.trim();
2482 if (! split.matches(".*\\(.*\\)\\.?")){
2483 fireWarningEvent("Common name string '"+split+"' does not match given pattern", state.getReader().peek(), 4);
2484 }
2485
2486 String name = split.replaceAll("\\(.*\\)", "").replace(".", "").trim();
2487 String languageStr = split.replaceFirst(".*\\(", "").replaceAll("\\)\\.?", "").trim();
2488
2489 Language language = null;
2490 if (StringUtils.isNotBlank(languageStr)){
2491 try {
2492 UUID langUuid = state.getTransformer().getLanguageUuid(languageStr);
2493 TermVocabulary<?> voc = null;
2494 language = getLanguage(state, langUuid, languageStr, languageStr, null, voc);
2495 if (language == null){
2496 logger.warn("Language " + languageStr + " not recognized by transformer");
2497 }
2498 } catch (UndefinedTransformerMethodException e) {
2499 throw new RuntimeException(e);
2500 }
2501 }
2502 NamedArea area = null;
2503 CommonTaxonName commonTaxonName = CommonTaxonName.NewInstance(name, language, area);
2504 result.add(commonTaxonName);
2505 }
2506
2507 return result;
2508 }
2509
2510 private List<DescriptionElementBase> handleMaterialsExamined(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
2511 List<DescriptionElementBase> result = new ArrayList<DescriptionElementBase>();
2512 while (reader.hasNext()) {
2513 XMLEvent next = readNoWhitespace(reader);
2514 if (isMyEndingElement(next, parentEvent)) {
2515 if (result.isEmpty()){
2516 fireWarningEvent("Materials examined created empty Individual Associations list", parentEvent, 4);
2517 }
2518 return result;
2519 } else if (isStartingElement(next, SUB_HEADING)) {
2520 handleNotYetImplementedElement(next);
2521 } else if (isStartingElement(next, BR)) {
2522 handleNotYetImplementedElement(next);
2523 } else if (isStartingElement(next, GATHERING)) {
2524 DerivedUnitFacade facade = DerivedUnitFacade.NewInstance(DerivedUnitType.DerivedUnit.DerivedUnit);
2525 handleGathering(state, reader, next, facade);
2526 SpecimenOrObservationBase<?> specimen;
2527 if (facade.innerDerivedUnit() != null){
2528 specimen = facade.innerDerivedUnit();
2529 }else{
2530 specimen = facade.innerFieldObservation();
2531 }
2532 IndividualsAssociation individualsAssociation = IndividualsAssociation.NewInstance();
2533 individualsAssociation.setAssociatedSpecimenOrObservation(specimen);
2534 result.add(individualsAssociation);
2535 } else {
2536 handleUnexpectedElement(next);
2537 }
2538 }
2539 throw new IllegalStateException("<String> has no closing tag");
2540
2541 }
2542
2543 /**
2544 * @param state
2545 * @param reader
2546 * @param feature
2547 * @param taxon
2548 * @param next
2549 * @throws XMLStreamException
2550 */
2551 private void makeFeatureWriter(MarkupImportState state,XMLEventReader reader, Feature feature, Taxon taxon, XMLEvent next) throws XMLStreamException {
2552 WriterDataHolder writer = handleWriter(state, reader, next);
2553 if (isNotBlank(writer.writer)) {
2554 // TODO
2555 Reference<?> ref = state.getConfig().getSourceReference();
2556 TaxonDescription description = getTaxonDescription(taxon, ref,
2557 false, true);
2558 TextData featurePlaceholder = docImport.getFeaturePlaceholder(state,
2559 description, feature, true);
2560 featurePlaceholder.addAnnotation(writer.annotation);
2561 registerFootnotes(state, featurePlaceholder, writer.footnotes);
2562 } else {
2563 String message = "Writer element is empty";
2564 fireWarningEvent(message, next, 4);
2565 }
2566 }
2567
2568 /**
2569 * @param state
2570 * @param reader
2571 * @param classValue
2572 * @param feature
2573 * @param next
2574 * @throws XMLStreamException
2575 */
2576 private void makeFeatureHeading(MarkupImportState state, XMLEventReader reader, String classValue, Feature feature, XMLEvent next) throws XMLStreamException {
2577 String heading = handleHeading(state, reader, next);
2578 if (StringUtils.isNotBlank(heading)) {
2579 if (!heading.equalsIgnoreCase(classValue)) {
2580 try {
2581 if (!feature.equals(state.getTransformer().getFeatureByKey(
2582 heading))) {
2583 UUID headerFeatureUuid = state.getTransformer()
2584 .getFeatureUuid(heading);
2585 if (!feature.getUuid().equals(headerFeatureUuid)) {
2586 String message = "Feature heading '%s' differs from feature class '%s' and can not be transformed to feature";
2587 message = String.format(message, heading,
2588 classValue);
2589 fireWarningEvent(message, next, 1);
2590 }
2591 }
2592 } catch (UndefinedTransformerMethodException e) {
2593 throw new RuntimeException(e);
2594 }
2595 } else {
2596 // do nothing
2597 }
2598 }
2599 }
2600
2601 private List<Reference<?>> handleReferences(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
2602 // attributes
2603 Map<String, Attribute> attributes = getAttributes(parentEvent);
2604 String bibliography = getAndRemoveAttributeValue(attributes,
2605 BIBLIOGRAPHY);
2606 String serialsAbbreviations = getAndRemoveAttributeValue(attributes,
2607 SERIALS_ABBREVIATIONS);
2608 if (isNotBlank(bibliography) || isNotBlank(serialsAbbreviations)) {
2609 String message = "Attributes not yet implemented for <references>";
2610 fireWarningEvent(message, parentEvent, 4);
2611 }
2612
2613 List<Reference<?>> result = new ArrayList<Reference<?>>();
2614
2615 // elements
2616 while (reader.hasNext()) {
2617 XMLEvent next = readNoWhitespace(reader);
2618 if (next.isEndElement()) {
2619 if (isMyEndingElement(next, parentEvent)) {
2620 return result;
2621 } else {
2622 if (isEndingElement(next, HEADING)) {
2623 // NOT YET IMPLEMENTED
2624 popUnimplemented(next.asEndElement());
2625 } else if (isEndingElement(next, WRITER)) {
2626 // NOT YET IMPLEMENTED
2627 popUnimplemented(next.asEndElement());
2628 } else if (isEndingElement(next, FOOTNOTE)) {
2629 // NOT YET IMPLEMENTED
2630 popUnimplemented(next.asEndElement());
2631 } else if (isEndingElement(next, STRING)) {
2632 // NOT YET IMPLEMENTED
2633 popUnimplemented(next.asEndElement());
2634 } else if (isEndingElement(next, REF_NUM)) {
2635 // NOT YET IMPLEMENTED
2636 popUnimplemented(next.asEndElement());
2637 } else {
2638 handleUnexpectedEndElement(next.asEndElement());
2639 }
2640 }
2641 } else if (next.isStartElement()) {
2642 if (isStartingElement(next, HEADING)) {
2643 handleNotYetImplementedElement(next);
2644 } else if (isStartingElement(next, SUB_HEADING)) {
2645 String subheading = getCData(state, reader, next).trim();
2646 String excludePattern = "(i?)(References?|Literature):?";
2647 if (!subheading.matches(excludePattern)) {
2648 fireNotYetImplementedElement(next.getLocation(), next.asStartElement().getName(), 0);
2649 }
2650 } else if (isStartingElement(next, WRITER)) {
2651 handleNotYetImplementedElement(next);
2652 } else if (isStartingElement(next, FOOTNOTE)) {
2653 handleNotYetImplementedElement(next);
2654 } else if (isStartingElement(next, STRING)) {
2655 handleNotYetImplementedElement(next);
2656 } else if (isStartingElement(next, REF_NUM)) {
2657 handleNotYetImplementedElement(next);
2658 } else if (isStartingElement(next, REFERENCE)) {
2659 Reference<?> ref = handleReference(state, reader, next);
2660 result.add(ref);
2661 } else {
2662 handleUnexpectedStartElement(next);
2663 }
2664 } else {
2665 handleUnexpectedElement(next);
2666 }
2667 }
2668 throw new IllegalStateException("<References> has no closing tag");
2669 }
2670
2671 private void handleHabitat(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
2672 checkNoAttributes(parentEvent);
2673 Taxon taxon = state.getCurrentTaxon();
2674 // TODO which ref to take?
2675 Reference<?> ref = state.getConfig().getSourceReference();
2676
2677 String text = "";
2678 while (reader.hasNext()) {
2679 XMLEvent next = readNoWhitespace(reader);
2680 if (isMyEndingElement(next, parentEvent)) {
2681 TaxonDescription description = getTaxonDescription(taxon, ref,
2682 false, true);
2683 UUID uuidExtractedHabitat = MarkupTransformer.uuidExtractedHabitat;
2684 Feature feature = getFeature(
2685 state,
2686 uuidExtractedHabitat,
2687 "Extracted Habitat",
2688 "An structured habitat that was extracted from a habitat text",
2689 "extr. habit.", null);
2690 TextData habitat = TextData.NewInstance(feature);
2691 habitat.putText(Language.DEFAULT(), text);
2692 description.addElement(habitat);
2693
2694 return;
2695 } else if (next.isStartElement()) {
2696 if (isStartingElement(next, ALTITUDE)) {
2697 text = text.trim() + getTaggedCData(state, reader, next);
2698 } else if (isStartingElement(next, LIFE_CYCLE_PERIODS)) {
2699 handleNotYetImplementedElement(next);
2700 } else {
2701 handleUnexpectedStartElement(next.asStartElement());
2702 }
2703 } else if (next.isCharacters()) {
2704 text += next.asCharacters().getData();
2705 } else {
2706 handleUnexpectedElement(next);
2707 }
2708 }
2709 throw new IllegalStateException("<Habitat> has no closing tag");
2710 }
2711
2712 private String getTaggedCData(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
2713 checkNoAttributes(parentEvent);
2714
2715 String text = getXmlTag(parentEvent);
2716 while (reader.hasNext()) {
2717 XMLEvent next = readNoWhitespace(reader);
2718 if (isMyEndingElement(next, parentEvent)) {
2719 text += getXmlTag(next);
2720 return text;
2721 } else if (next.isStartElement()) {
2722 text += getTaggedCData(state, reader, next);
2723 } else if (next.isEndElement()) {
2724 text += getTaggedCData(state, reader, next);
2725 } else if (next.isCharacters()) {
2726 text += next.asCharacters().getData();
2727 } else {
2728 handleUnexpectedEndElement(next.asEndElement());
2729 }
2730 }
2731 throw new IllegalStateException("Some tag has no closing tag");
2732 }
2733
2734 private String handleDistributionLocality(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent)throws XMLStreamException {
2735 Map<String, Attribute> attributes = getAttributes(parentEvent);
2736 String classValue = getAndRemoveRequiredAttributeValue(parentEvent, attributes, CLASS);
2737 String statusValue =getAndRemoveAttributeValue(attributes, STATUS);
2738 String frequencyValue =getAndRemoveAttributeValue(attributes, FREQUENCY);
2739
2740
2741 Taxon taxon = state.getCurrentTaxon();
2742 // TODO which ref to take?
2743 Reference<?> ref = state.getConfig().getSourceReference();
2744
2745 String text = "";
2746 while (reader.hasNext()) {
2747 XMLEvent next = readNoWhitespace(reader);
2748 if (isMyEndingElement(next, parentEvent)) {
2749 if (StringUtils.isNotBlank(text)) {
2750 String label = CdmUtils.removeTrailingDot(normalize(text));
2751 TaxonDescription description = getTaxonDescription(taxon, ref, false, true);
2752 NamedAreaLevel level = makeNamedAreaLevel(state,classValue, next);
2753
2754 //status
2755 PresenceAbsenceTermBase<?> status = null;
2756 if (isNotBlank(statusValue)){
2757 try {
2758 status = state.getTransformer().getPresenceTermByKey(statusValue);
2759 if (status == null){
2760 //TODO
2761 String message = "The status '%s' could not be transformed to an CDM status";
2762 fireWarningEvent(message, next, 4);
2763 }
2764 } catch (UndefinedTransformerMethodException e) {
2765 throw new RuntimeException(e);
2766 }
2767 }else{
2768 status = PresenceTerm.PRESENT();
2769 }
2770 //frequency
2771 if (isNotBlank(frequencyValue)){
2772 String message = "The frequency attribute is currently not yet available in CDM";
2773 fireWarningEvent(message, parentEvent, 6);
2774 }
2775
2776 NamedArea higherArea = null;
2777 List<NamedArea> areas = new ArrayList<NamedArea>();
2778
2779 String patSingleArea = "([^,\\(]{3,})";
2780 String patSeparator = "(,|\\sand\\s)";
2781 String hierarchiePattern = String.format("%s\\((%s(%s%s)*)\\)",patSingleArea, patSingleArea, patSeparator, patSingleArea);
2782 Pattern patHierarchie = Pattern.compile(hierarchiePattern, Pattern.CASE_INSENSITIVE);
2783 Matcher matcher = patHierarchie.matcher(label);
2784 if (matcher.matches()){
2785 String higherAreaStr = matcher.group(1).trim();
2786 higherArea = makeArea(state, higherAreaStr, level);
2787 String[] innerAreas = matcher.group(2).split(patSeparator);
2788 for (String innerArea : innerAreas){
2789 if (isNotBlank(innerArea)){
2790 NamedArea singleArea = makeArea(state, innerArea.trim(), level);
2791 areas.add(singleArea);
2792 NamedArea partOf = singleArea.getPartOf();
2793 // if (partOf == null){
2794 // singleArea.setPartOf(higherArea);
2795 // }
2796 }
2797 }
2798 }else{
2799 NamedArea singleArea = makeArea(state, label, level);
2800 areas.add(singleArea);
2801 }
2802
2803 for (NamedArea area : areas){
2804 //create distribution
2805 Distribution distribution = Distribution.NewInstance(area,status);
2806 description.addElement(distribution);
2807 }
2808 } else {
2809 String message = "Empty distribution locality";
2810 fireWarningEvent(message, next, 4);
2811 }
2812 return text;
2813 } else if (isStartingElement(next, COORDINATES)) {
2814 //TODO
2815 handleNotYetImplementedElement(next);
2816 } else if (isEndingElement(next, COORDINATES)) {
2817 //TODO
2818 popUnimplemented(next.asEndElement());
2819 } else if (next.isCharacters()) {
2820 text += next.asCharacters().getData();
2821 } else {
2822 handleUnexpectedElement(next);
2823 }
2824 }
2825 throw new IllegalStateException("<DistributionLocality> has no closing tag");
2826 }
2827
2828 /**
2829 * @param state
2830 * @param areaName
2831 * @param level
2832 * @return
2833 */
2834 private NamedArea makeArea(MarkupImportState state, String areaName, NamedAreaLevel level) {
2835
2836
2837 //TODO FM vocabulary
2838 TermVocabulary<NamedArea> voc = null;
2839 NamedAreaType areaType = null;
2840
2841 NamedArea area = null;
2842 try {
2843 area = state.getTransformer().getNamedAreaByKey(areaName);
2844 } catch (UndefinedTransformerMethodException e) {
2845 throw new RuntimeException(e);
2846 }
2847 if (area == null){
2848 boolean isNewInState = false;
2849 UUID uuid = state.getAreaUuid(areaName);
2850 if (uuid == null){
2851 isNewInState = true;
2852
2853
2854 try {
2855 uuid = state.getTransformer().getNamedAreaUuid(areaName);
2856 } catch (UndefinedTransformerMethodException e) {
2857 throw new RuntimeException(e);
2858 }
2859 }
2860
2861 CdmImportBase.TermMatchMode matchMode = CdmImportBase.TermMatchMode.UUID_LABEL;
2862 area = getNamedArea(state, uuid, areaName, areaName, areaName, areaType, level, voc, matchMode);
2863 if (isNewInState){
2864 state.putAreaUuid(areaName, area.getUuid());
2865
2866 //TODO just for testing -> make generic and move to better place
2867 String geoServiceLayer="vmap0_as_bnd_political_boundary_a";
2868 String layerFieldName ="nam";
2869
2870 if ("Bangka".equals(areaName)){
2871 String areaValue = "PULAU BANGKA#SUMATERA SELATAN";
2872 GeoServiceArea geoServiceArea = new GeoServiceArea();
2873 geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);
2874 this.editGeoService.setMapping(area, geoServiceArea);
2875 // save(area, state);
2876 }
2877 if ("Luzon".equals(areaName)){
2878 GeoServiceArea geoServiceArea = new GeoServiceArea();
2879
2880 List<String> list = Arrays.asList("HERMANA MAYOR ISLAND#CENTRAL LUZON",
2881 "HERMANA MENOR ISLAND#CENTRAL LUZON",
2882 "CENTRAL LUZON");
2883 for (String areaValue : list){
2884 geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);
2885 }
2886
2887 this.editGeoService.setMapping(area, geoServiceArea);
2888 // save(area, state);
2889 }
2890 if ("Mindanao".equals(areaName)){
2891 GeoServiceArea geoServiceArea = new GeoServiceArea();
2892
2893 List<String> list = Arrays.asList("NORTHERN MINDANAO",
2894 "SOUTHERN MINDANAO",
2895 "WESTERN MINDANAO");
2896 //TODO to be continued
2897 for (String areaValue : list){
2898 geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);
2899 }
2900
2901 this.editGeoService.setMapping(area, geoServiceArea);
2902 // save(area, state);
2903 }
2904 if ("Palawan".equals(areaName)){
2905 GeoServiceArea geoServiceArea = new GeoServiceArea();
2906
2907 List<String> list = Arrays.asList("PALAWAN#SOUTHERN TAGALOG");
2908 for (String areaValue : list){
2909 geoServiceArea.add(geoServiceLayer, layerFieldName, areaValue);
2910 }
2911
2912 this.editGeoService.setMapping(area, geoServiceArea);
2913 // save(area, state);
2914 }
2915
2916
2917 }
2918 }
2919 return area;
2920 }
2921
2922
2923 /**
2924 * @param state
2925 * @param levelString
2926 * @param next
2927 * @return
2928 */
2929 private NamedAreaLevel makeNamedAreaLevel(MarkupImportState state,
2930 String levelString, XMLEvent next) {
2931 NamedAreaLevel level;
2932 try {
2933 level = state.getTransformer().getNamedAreaLevelByKey(levelString);
2934 if (level == null) {
2935 UUID levelUuid = state.getTransformer().getNamedAreaLevelUuid(levelString);
2936 if (levelUuid == null) {
2937 String message = "Unknown distribution locality class (named area level): %s. Create new level instead.";
2938 message = String.format(message, levelString);
2939 fireWarningEvent(message, next, 6);
2940 }
2941 level = getNamedAreaLevel(state, levelUuid, levelString, levelString, levelString, null);
2942 }
2943 } catch (UndefinedTransformerMethodException e) {
2944 throw new RuntimeException(e);
2945 }
2946 return level;
2947 }
2948
2949 private String handleHeading(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent)throws XMLStreamException {
2950 checkNoAttributes(parentEvent);
2951
2952 String text = "";
2953 while (reader.hasNext()) {
2954 XMLEvent next = readNoWhitespace(reader);
2955 if (isMyEndingElement(next, parentEvent)) {
2956 return text;
2957 } else if (next.isStartElement()) {
2958 if (isStartingElement(next, FOOTNOTE)) {
2959 handleNotYetImplementedElement(next);
2960 } else {
2961 handleUnexpectedStartElement(next.asStartElement());
2962 }
2963 } else if (next.isCharacters()) {
2964 text += next.asCharacters().getData();
2965 } else {
2966 handleUnexpectedEndElement(next.asEndElement());
2967 }
2968 }
2969 throw new IllegalStateException("<String> has no closing tag");
2970
2971 }
2972
2973 /**
2974 * Handle string
2975 * @param state
2976 * @param reader
2977 * @param parentEvent
2978 * @param feature only needed for distributionLocalities
2979 * @return
2980 * @throws XMLStreamException
2981 */
2982 private Map<String, String> handleString(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, Feature feature)throws XMLStreamException {
2983 // attributes
2984 String classValue = getClassOnlyAttribute(parentEvent, false);
2985 if (StringUtils.isNotBlank(classValue)) {
2986 String message = "class attribute for <string> not yet implemented";
2987 fireWarningEvent(message, parentEvent, 2);
2988 }
2989
2990 // subheadings
2991 Map<String, String> subHeadingMap = new HashMap<String, String>();
2992 String currentSubheading = null;
2993
2994 boolean isTextMode = true;
2995 String text = "";
2996 while (reader.hasNext()) {
2997 XMLEvent next = readNoWhitespace(reader);
2998 if (isMyEndingElement(next, parentEvent)) {
2999 putCurrentSubheading(subHeadingMap, currentSubheading, text);
3000 return subHeadingMap;
3001 } else if (isStartingElement(next, BR)) {
3002 text += "<br/>";
3003 isTextMode = false;
3004 } else if (isEndingElement(next, BR)) {
3005 isTextMode = true;
3006 } else if (isHtml(next)) {
3007 text += getXmlTag(next);
3008 } else if (isStartingElement(next, SUB_HEADING)) {
3009 text = putCurrentSubheading(subHeadingMap,currentSubheading, text);
3010 // TODO footnotes
3011 currentSubheading = getCData(state, reader, next).trim();
3012 } else if (isStartingElement(next, DISTRIBUTION_LOCALITY)) {
3013 if (feature != null && !feature.equals(Feature.DISTRIBUTION())) {
3014 String message = "Distribution locality only allowed for feature of type 'distribution'";
3015 fireWarningEvent(message, next, 4);
3016 }
3017 text += handleDistributionLocality(state, reader, next);
3018 } else if (next.isCharacters()) {
3019 if (!isTextMode) {
3020 String message = "String is not in text mode";
3021 fireWarningEvent(message, next, 6);
3022 } else {
3023 text += next.asCharacters().getData();
3024 }
3025 } else if (isStartingElement(next, HEADING)) {
3026 //TODO
3027 handleNotYetImplementedElement(next);
3028 } else if (isEndingElement(next, HEADING)) {
3029 //TODO
3030 popUnimplemented(next.asEndElement());
3031 } else if (isStartingElement(next, QUOTE)) {
3032 //TODO
3033 handleNotYetImplementedElement(next);
3034 } else if (isEndingElement(next, QUOTE)) {
3035 //TODO
3036 popUnimplemented(next.asEndElement());
3037 } else if (isStartingElement(next, DEDICATION)) {
3038 //TODO
3039 handleNotYetImplementedElement(next);
3040 } else if (isEndingElement(next, DEDICATION)) {
3041 //TODO
3042 popUnimplemented(next.asEndElement());
3043 } else if (isStartingElement(next, TAXONTYPE)) {
3044 //TODO
3045 handleNotYetImplementedElement(next);
3046 } else if (isEndingElement(next, TAXONTYPE)) {
3047 //TODO
3048 popUnimplemented(next.asEndElement());
3049 } else if (isStartingElement(next, FULL_NAME)) {
3050 //TODO
3051 handleNotYetImplementedElement(next);
3052 } else if (isEndingElement(next, FULL_NAME)) {
3053 //TODO
3054 popUnimplemented(next.asEndElement());
3055 }else if (isStartingElement(next, REFERENCES)) {
3056 //TODO
3057 handleNotYetImplementedElement(next);
3058 } else if (isEndingElement(next, REFERENCES)) {
3059 //TODO
3060 popUnimplemented(next.asEndElement());
3061 } else if (isStartingElement(next, GATHERING)) {
3062 //TODO
3063 handleNotYetImplementedElement(next);
3064 } else if (isEndingElement(next, GATHERING)) {
3065 //TODO
3066 popUnimplemented(next.asEndElement());
3067 } else if (isStartingElement(next, ANNOTATION)) {
3068 //TODO
3069 handleNotYetImplementedElement(next);
3070 } else if (isEndingElement(next, ANNOTATION)) {
3071 //TODO
3072 popUnimplemented(next.asEndElement());
3073 } else if (isStartingElement(next, HABITAT)) {
3074 //TODO
3075 handleNotYetImplementedElement(next);
3076 } else if (isEndingElement(next, HABITAT)) {
3077 //TODO
3078 popUnimplemented(next.asEndElement());
3079 } else if (isStartingElement(next, FIGURE_REF)) {
3080 //TODO
3081 handleNotYetImplementedElement(next);
3082 } else if (isEndingElement(next, FIGURE_REF)) {
3083 //TODO
3084 popUnimplemented(next.asEndElement());
3085 } else if (isStartingElement(next, FIGURE)) {
3086 //TODO
3087 handleNotYetImplementedElement(next);
3088 } else if (isEndingElement(next, FIGURE)) {
3089 //TODO
3090 popUnimplemented(next.asEndElement());
3091 } else if (isStartingElement(next, FOOTNOTE_REF)) {
3092 //TODO
3093 handleNotYetImplementedElement(next);
3094 } else if (isEndingElement(next, FOOTNOTE_REF)) {
3095 //TODO
3096 popUnimplemented(next.asEndElement());
3097 } else if (isStartingElement(next, FOOTNOTE)) {
3098 //TODO
3099 handleNotYetImplementedElement(next);
3100 } else if (isEndingElement(next, FOOTNOTE)) {
3101 //TODO
3102 popUnimplemented(next.asEndElement());
3103 } else if (isStartingElement(next, WRITER)) {
3104 //TODO
3105 handleNotYetImplementedElement(next);
3106 } else if (isEndingElement(next, WRITER)) {
3107 //TODO
3108 popUnimplemented(next.asEndElement());
3109 } else if (isStartingElement(next, DATES)) {
3110 //TODO
3111 handleNotYetImplementedElement(next);
3112 } else if (isEndingElement(next, DATES)) {
3113 //TODO
3114 popUnimplemented(next.asEndElement());
3115 } else {
3116 handleUnexpectedElement(next);
3117 }
3118 }
3119 throw new IllegalStateException("<String> has no closing tag");
3120 }
3121
3122 /**
3123 * @param subHeadingMap
3124 * @param currentSubheading
3125 * @param text
3126 * @return
3127 */
3128 private String putCurrentSubheading(Map<String, String> subHeadingMap, String currentSubheading, String text) {
3129 if (StringUtils.isNotBlank(text)) {
3130 text = removeStartingMinus(text);
3131 subHeadingMap.put(currentSubheading, text.trim());
3132 }
3133 return "";
3134 }
3135
3136 private String removeStartingMinus(String string) {
3137 string = replaceStart(string, "-");
3138 string = replaceStart(string, "\u002d");
3139 string = replaceStart(string, "\u2013");
3140 string = replaceStart(string, "\u2014");
3141 string = replaceStart(string, "--");
3142 return string;
3143 }
3144
3145 /**
3146 * @param value
3147 * @param replacementString
3148 */
3149 private String replaceStart(String value, String replacementString) {
3150 if (value.startsWith(replacementString) ){
3151 value = value.substring(replacementString.length()).trim();
3152 }
3153 while (value.startsWith("-") || value.startsWith("\u2014") ){
3154 value = value.substring("-".length()).trim();
3155 }
3156 return value;
3157 }
3158
3159 private String getXmlTag(XMLEvent event) {
3160 String result;
3161 if (event.isStartElement()) {
3162 result = "<" + event.asStartElement().getName().getLocalPart()
3163 + ">";
3164 } else if (event.isEndElement()) {
3165 result = "</" + event.asEndElement().getName().getLocalPart() + ">";
3166 } else {
3167 String message = "Only start or end elements are allowed as Html tags";
3168 throw new IllegalStateException(message);
3169 }
3170 return result;
3171 }
3172
3173 protected static final List<String> htmlList = Arrays.asList("sub", "sup",
3174 "ol", "ul", "li", "i", "b", "table", "br","tr","td");
3175
3176 private boolean isHtml(XMLEvent event) {
3177 if (event.isStartElement()) {
3178 String tag = event.asStartElement().getName().getLocalPart();
3179 return htmlList.contains(tag);
3180 } else if (event.isEndElement()) {
3181 String tag = event.asEndElement().getName().getLocalPart();
3182 return htmlList.contains(tag);
3183 } else {
3184 return false;
3185 }
3186
3187 }
3188
3189 private TextData handleChar(MarkupImportState state, XMLEventReader reader,
3190 XMLEvent parentEvent) throws XMLStreamException {
3191 String classValue = getClassOnlyAttribute(parentEvent);
3192 Feature feature = makeFeature(classValue, state, parentEvent);
3193
3194 boolean isTextMode = true;
3195 String text = "";
3196 while (reader.hasNext()) {
3197 XMLEvent next = readNoWhitespace(reader);
3198 if (isMyEndingElement(next, parentEvent)) {
3199 TextData textData = TextData.NewInstance(feature);
3200 textData.putText(Language.DEFAULT(), text);
3201 return textData;
3202 } else if (isStartingElement(next, FIGURE_REF)) {
3203 //TODO
3204 handleNotYetImplementedElement(next);
3205 } else if (isEndingElement(next, FIGURE_REF)) {
3206 //TODO
3207 popUnimplemented(next.asEndElement());
3208 } else if (isStartingElement(next, FOOTNOTE_REF)) {
3209 //TODO
3210 handleNotYetImplementedElement(next);
3211 } else if (isEndingElement(next, FOOTNOTE_REF)) {
3212 //TODO
3213 popUnimplemented(next.asEndElement());
3214 } else if (isStartingElement(next, BR)) {
3215 text += "<br/>";
3216 isTextMode = false;
3217 } else if (isEndingElement(next, BR)) {
3218 isTextMode = true;
3219 } else if (isHtml(next)) {
3220 text += getXmlTag(next);
3221 } else if (next.isStartElement()) {
3222 if (isStartingElement(next, ANNOTATION)) {
3223 handleNotYetImplementedElement(next);
3224 } else if (isStartingElement(next, ITALICS)) {
3225 handleNotYetImplementedElement(next);
3226 } else if (isStartingElement(next, BOLD)) {
3227 handleNotYetImplementedElement(next);
3228 } else if (isStartingElement(next, FIGURE)) {
3229 handleFigure(state, reader, next);
3230 } else if (isStartingElement(next, FOOTNOTE)) {
3231 FootnoteDataHolder footnote = handleFootnote(state, reader, next);
3232 if (footnote.isRef()) {
3233 String message = "Ref footnote not implemented here";
3234 fireWarningEvent(message, next, 4);
3235 } else {
3236 registerGivenFootnote(state, footnote);
3237 }
3238 } else {
3239 handleUnexpectedStartElement(next.asStartElement());
3240 }
3241 } else if (next.isCharacters()) {
3242 if (!isTextMode) {
3243 String message = "String is not in text mode";
3244 fireWarningEvent(message, next, 6);
3245 } else {
3246 text += next.asCharacters().getData();
3247 }
3248 } else {
3249 handleUnexpectedEndElement(next.asEndElement());
3250 }
3251 }
3252 throw new IllegalStateException("RefPart has no closing tag");
3253 }
3254
3255 /**
3256 * @param classValue
3257 * @param state
3258 * @param parentEvent
3259 * @return
3260 * @throws UndefinedTransformerMethodException
3261 */
3262 private Feature makeFeature(String classValue, MarkupImportState state, XMLEvent parentEvent) {
3263 UUID uuid;
3264 try {
3265 Feature feature = state.getTransformer().getFeatureByKey(classValue);
3266 if (feature != null) {
3267 return feature;
3268 }
3269 uuid = state.getTransformer().getFeatureUuid(classValue);
3270 if (uuid == null) {
3271 // TODO
3272 String message = "Uuid is not defined for '%s'";
3273 message = String.format(message, classValue);
3274 fireWarningEvent(message, parentEvent, 8);
3275 }
3276 String featureText = StringUtils.capitalize(classValue);
3277
3278 // TODO eFlora vocabulary
3279 TermVocabulary<Feature> voc = null;
3280 feature = getFeature(state, uuid, featureText, featureText, classValue, voc);
3281 if (feature == null) {
3282 throw new NullPointerException(classValue + " not recognized as a feature");
3283 }
3284 return feature;
3285 } catch (Exception e) {
3286 String message = "Could not create feature for %s: %s";
3287 message = String.format(message, classValue, e.getMessage());
3288 fireWarningEvent(message, parentEvent, 4);
3289 return Feature.UNKNOWN();
3290 }
3291 }
3292
3293
3294
3295 }