Project

General

Profile

« Previous | Next » 

Revision 786696e3

Added by Andreas Müller over 10 years ago

merge trunk into cdm3.3 branch

View differences:

cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/markup/MarkupDocumentImportNoComponent.java
11 11

  
12 12
import java.net.MalformedURLException;
13 13
import java.net.URL;
14
import java.util.ArrayList;
15
import java.util.Arrays;
16
import java.util.HashMap;
17 14
import java.util.HashSet;
18
import java.util.List;
19 15
import java.util.Map;
20 16
import java.util.Set;
21 17
import java.util.UUID;
22
import java.util.regex.Matcher;
23
import java.util.regex.Pattern;
24 18

  
25 19
import javax.xml.stream.Location;
26 20
import javax.xml.stream.XMLEventReader;
......
32 26
import org.apache.commons.lang.StringUtils;
33 27
import org.apache.log4j.Logger;
34 28

  
35
import eu.etaxonomy.cdm.common.CdmUtils;
36 29
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
37 30
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
38
import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
39
import eu.etaxonomy.cdm.model.common.Annotation;
40
import eu.etaxonomy.cdm.model.common.AnnotationType;
41
import eu.etaxonomy.cdm.model.common.CdmBase;
42
import eu.etaxonomy.cdm.model.common.Extension;
43 31
import eu.etaxonomy.cdm.model.common.ExtensionType;
44 32
import eu.etaxonomy.cdm.model.common.Language;
45 33
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
......
50 38
import eu.etaxonomy.cdm.model.description.Feature;
51 39
import eu.etaxonomy.cdm.model.description.PolytomousKey;
52 40
import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
53
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;
54
import eu.etaxonomy.cdm.model.description.PresenceTerm;
55 41
import eu.etaxonomy.cdm.model.description.TaxonDescription;
56 42
import eu.etaxonomy.cdm.model.description.TextData;
57
import eu.etaxonomy.cdm.model.location.NamedArea;
58
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
59
import eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity;
60
import eu.etaxonomy.cdm.model.media.Media;
61 43
import eu.etaxonomy.cdm.model.name.CultivarPlantName;
62 44
import eu.etaxonomy.cdm.model.name.NonViralName;
63 45
import eu.etaxonomy.cdm.model.name.Rank;
64
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
65 46
import eu.etaxonomy.cdm.model.reference.Reference;
66 47
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
67 48
import eu.etaxonomy.cdm.model.taxon.Classification;
68 49
import eu.etaxonomy.cdm.model.taxon.Taxon;
69 50
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
70
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
71 51

  
72 52

  
73 53
/**
......
75 55
 * 
76 56
 */
77 57
public class MarkupDocumentImportNoComponent extends MarkupImportBase {
58
	@SuppressWarnings("unused")
78 59
	private static final Logger logger = Logger.getLogger(MarkupDocumentImportNoComponent.class);
79 60
	
80 61
	private MarkupKeyImport keyImport;
81
	private MarkupSpecimenImport specimenImport;
82 62

  
63
	private MarkupModsImport modsImport;
64
	private MarkupFeatureImport featureImport;
65
	private MarkupSpecimenImport specimenImport;
83 66
	private MarkupNomenclatureImport nomenclatureImport;
84
	
67

  
85 68
	public MarkupDocumentImportNoComponent(MarkupDocumentImport docImport) {
86 69
		super(docImport);
87 70
		this.keyImport = new MarkupKeyImport(docImport);
88 71
		this.specimenImport = new MarkupSpecimenImport(docImport);
89
		nomenclatureImport = new MarkupNomenclatureImport(docImport, keyImport, specimenImport);
72
		this.nomenclatureImport = new MarkupNomenclatureImport(docImport, specimenImport);
73
		this.modsImport = new MarkupModsImport(docImport);
74
		this.featureImport = new MarkupFeatureImport(docImport, specimenImport, nomenclatureImport);
90 75
	}
91 76

  
92 77
	public void doInvoke(MarkupImportState state) throws XMLStreamException { 
......
193 178
					message = String.format(message, baseUrl);
194 179
					fireWarningEvent(message, next, 8);
195 180
				}
196
			} else if (isStartingElement(next, MODS)){
197
				handleNotYetImplementedElement(next);
181
			} else if (isStartingElement(next, MODS)){	
182
				modsImport.handleMods(state, reader, next);
198 183
			} else {
199 184
				handleUnexpectedElement(next);
200 185
			}
......
398 383
					nomenclatureImport.handleNomenclature(state, reader, next);
399 384
					hasNomenclature = true;
400 385
				} else if (isStartingElement(next, FEATURE)) {
401
					handleFeature(state, reader, next);
386
					featureImport.handleFeature(state, reader, next);
402 387
				} else if (isStartingElement(next, NOTES)) {
403 388
					// TODO is this the correct way to handle notes?
404 389
					String note = handleNotes(state, reader, next);
......
425 410
						desc.addElement(textData);
426 411
					}
427 412
					textData = (TextData)desc.getElements().iterator().next();
428
					makeFeatureFigureRef(state, reader, desc, false, textData, next);
413
					featureImport.makeFeatureFigureRef(state, reader, desc, false, textData, next);
429 414
				} else if (isStartingElement(next, FIGURE)) {
430
					handleFigure(state, reader, next);
415
					handleFigure(state, reader, next, specimenImport, nomenclatureImport);
431 416
				} else if (isStartingElement(next, FOOTNOTE)) {
432
					FootnoteDataHolder footnote = handleFootnote(state, reader,	next);
417
					FootnoteDataHolder footnote = handleFootnote(state, reader,	next, specimenImport, nomenclatureImport);
433 418
					if (footnote.isRef()) {
434 419
						String message = "Ref footnote not implemented here";
435 420
						fireWarningEvent(message, next, 4);
......
458 443
		WriterDataHolder writer = handleWriter(state, reader, next);
459 444
		taxon.addExtension(writer.extension);
460 445
		// TODO what if taxonTitle comes later
461
		if (StringUtils.isNotBlank(taxonTitle)
462
				&& writer.extension != null) {
446
		if (StringUtils.isNotBlank(taxonTitle) && writer.extension != null) {
463 447
			Reference<?> sec = ReferenceFactory.newBookSection();
464 448
			sec.setTitle(taxonTitle);
465 449
			TeamOrPersonBase<?> author = createAuthor(writer.writer);
466 450
			sec.setAuthorTeam(author);
467
			sec.setInReference(state.getConfig()
468
					.getSourceReference());
451
			sec.setInReference(state.getConfig().getSourceReference());
469 452
			taxon.setSec(sec);
470 453
			registerFootnotes(state, sec, writer.footnotes);
471 454
		} else {
......
645 628

  
646 629
	}
647 630

  
648
	private WriterDataHolder handleWriter(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
649
		String text = "";
650
		checkNoAttributes(parentEvent);
651
		WriterDataHolder dataHolder = new WriterDataHolder();
652
		List<FootnoteDataHolder> footnotes = new ArrayList<FootnoteDataHolder>();
653

  
654
		// TODO handle attributes
655
		while (reader.hasNext()) {
656
			XMLEvent next = readNoWhitespace(reader);
657
			if (isMyEndingElement(next, parentEvent)) {
658
				text = CdmUtils.removeBrackets(text);
659
				if (checkMandatoryText(text, parentEvent)) {
660
					text = normalize(text);
661
					dataHolder.writer = text;
662
					dataHolder.footnotes = footnotes;
663

  
664
					// Extension
665
					UUID uuidWriterExtension = MarkupTransformer.uuidWriterExtension;
666
					ExtensionType writerExtensionType = this
667
							.getExtensionType(state, uuidWriterExtension,
668
									"Writer", "writer", "writer");
669
					Extension extension = Extension.NewInstance();
670
					extension.setType(writerExtensionType);
671
					extension.setValue(text);
672
					dataHolder.extension = extension;
673

  
674
					// Annotation
675
					UUID uuidWriterAnnotation = MarkupTransformer.uuidWriterAnnotation;
676
					AnnotationType writerAnnotationType = this.getAnnotationType(state, uuidWriterAnnotation, "Writer", "writer", "writer", null);
677
					Annotation annotation = Annotation.NewInstance(text, writerAnnotationType, getDefaultLanguage(state));
678
					dataHolder.annotation = annotation;
679

  
680
					return dataHolder;
681
				} else {
682
					return null;
683
				}
684
			} else if (isStartingElement(next, FOOTNOTE_REF)) {
685
				FootnoteDataHolder footNote = handleFootnoteRef(state, reader, next);
686
				if (footNote.isRef()) {
687
					footnotes.add(footNote);
688
				} else {
689
					logger.warn("Non ref footnotes not yet impelemnted");
690
				}
691
			} else if (next.isCharacters()) {
692
				text += next.asCharacters().getData();
693

  
694
			} else {
695
				handleUnexpectedElement(next);
696
				state.setUnsuccessfull();
697
			}
698
		}
699
		throw new IllegalStateException("<writer> has no end tag");
700
	}
701

  
702
	private void registerFootnotes(MarkupImportState state, AnnotatableEntity entity, List<FootnoteDataHolder> footnotes) {
703
		for (FootnoteDataHolder footNote : footnotes) {
704
			registerFootnoteDemand(state, entity, footNote);
705
		}
706
	}
707

  
708
	private void registerGivenFootnote(MarkupImportState state, FootnoteDataHolder footnote) {
709
		state.registerFootnote(footnote);
710
		Set<AnnotatableEntity> demands = state.getFootnoteDemands(footnote.id);
711
		if (demands != null) {
712
			for (AnnotatableEntity entity : demands) {
713
				attachFootnote(state, entity, footnote);
714
			}
715
		}
716
	}
717

  
718
	private void registerGivenFigure(MarkupImportState state, XMLEvent next, String id, Media figure) {
719
		state.registerFigure(id, figure);
720
		Set<AnnotatableEntity> demands = state.getFigureDemands(id);
721
		if (demands != null) {
722
			for (AnnotatableEntity entity : demands) {
723
				attachFigure(state, next, entity, figure);
724
			}
725
		}
726
		save(figure, state);
727
	}
728

  
729
	private void registerFootnoteDemand(MarkupImportState state, AnnotatableEntity entity, FootnoteDataHolder footnote) {
730
		FootnoteDataHolder existingFootnote = state.getFootnote(footnote.ref);
731
		if (existingFootnote != null) {
732
			attachFootnote(state, entity, existingFootnote);
733
		} else {
734
			Set<AnnotatableEntity> demands = state.getFootnoteDemands(footnote.ref);
735
			if (demands == null) {
736
				demands = new HashSet<AnnotatableEntity>();
737
				state.putFootnoteDemands(footnote.ref, demands);
738
			}
739
			demands.add(entity);
740
		}
741
	}
742

  
743
	private void registerFigureDemand(MarkupImportState state, XMLEvent next, AnnotatableEntity entity, String figureRef) {
744
		Media existingFigure = state.getFigure(figureRef);
745
		if (existingFigure != null) {
746
			attachFigure(state, next, entity, existingFigure);
747
		} else {
748
			Set<AnnotatableEntity> demands = state.getFigureDemands(figureRef);
749
			if (demands == null) {
750
				demands = new HashSet<AnnotatableEntity>();
751
				state.putFigureDemands(figureRef, demands);
752
			}
753
			demands.add(entity);
754
		}
755
	}
756

  
757
	private void attachFootnote(MarkupImportState state, AnnotatableEntity entity, FootnoteDataHolder footnote) {
758
		AnnotationType annotationType = this.getAnnotationType(state, MarkupTransformer.uuidFootnote, "Footnote", "An e-flora footnote", "fn", null);
759
		Annotation annotation = Annotation.NewInstance(footnote.string, annotationType, getDefaultLanguage(state));
760
		// TODO transient objects
761
		entity.addAnnotation(annotation);
762
		save(entity, state);
763
	}
764

  
765
	private void attachFigure(MarkupImportState state, XMLEvent next, AnnotatableEntity entity, Media figure) {
766
		// IdentifiableEntity<?> toSave;
767
		if (entity.isInstanceOf(TextData.class)) {
768
			TextData deb = CdmBase.deproxy(entity, TextData.class);
769
			deb.addMedia(figure);
770
			// toSave = ((TaxonDescription)deb.getInDescription()).getTaxon();
771
		} else if (entity.isInstanceOf(SpecimenOrObservationBase.class)) {
772
			String message = "figures for specimen should be handled as Textdata";
773
			fireWarningEvent(message, next, 4);
774
			// toSave = ime;
775
		} else if (entity.isInstanceOf(IdentifiableMediaEntity.class)) {
776
			IdentifiableMediaEntity<?> ime = CdmBase.deproxy(entity, IdentifiableMediaEntity.class);
777
			ime.addMedia(figure);
778
			// toSave = ime;
779
		} else {
780
			String message = "Unsupported entity to attach media: %s";
781
			message = String.format(message, entity.getClass().getName());
782
			// toSave = null;
783
		}
784
		save(entity, state);
785
	}
786

  
787
	private Media handleFigure(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
788
		// FigureDataHolder result = new FigureDataHolder();
789

  
790
		Map<String, Attribute> attributes = getAttributes(parentEvent);
791
		String id = getAndRemoveAttributeValue(attributes, ID);
792
		String type = getAndRemoveAttributeValue(attributes, TYPE);
793
		String urlAttr = getAndRemoveAttributeValue(attributes, URL);
794
		checkNoAttributes(attributes, parentEvent);
795

  
796
		String urlString = null;
797
		String legendString = null;
798
		String titleString = null;
799
		String numString = null;
800
		String text = null;
801
		if (isNotBlank(urlAttr)){
802
			urlString = CdmUtils.Nz(state.getBaseMediaUrl()) + urlAttr;
803
		}
804
		while (reader.hasNext()) {
805
			XMLEvent next = readNoWhitespace(reader);
806
			if (isMyEndingElement(next, parentEvent)) {
807
				if (isNotBlank(text)){
808
					fireWarningEvent("Text not yet handled for figures: " + text, next, 4);
809
				}
810
				Media media = makeFigure(state, id, type, urlString, legendString, titleString, numString, next);
811
				return media;
812
			} else if (isStartingElement(next, FIGURE_LEGEND)) {
813
				// TODO same as figure string ?
814
				legendString = handleFootnoteString(state, reader, next);
815
			} else if (isStartingElement(next, FIGURE_TITLE)) {
816
				titleString = getCData(state, reader, next);
817
			} else if (isStartingElement(next, URL)) {
818
				String localUrl = getCData(state, reader, next);
819
				String url = CdmUtils.Nz(state.getBaseMediaUrl()) + localUrl;
820
				if (isBlank(urlString)){
821
					urlString = url;
822
				}
823
				if (! url.equals(urlString)){
824
					String message = "URL attribute and URL element differ. Attribute: %s, Element: %s";
825
					fireWarningEvent(String.format(message, urlString, url), next, 2);
826
				}
827
			} else if (isStartingElement(next, NUM)) {
828
				numString = getCData(state, reader, next);
829
			} else if (next.isCharacters()) {
830
				text += CdmUtils.concat("", text, next.asCharacters().getData());
831
			} else {
832
				fireUnexpectedEvent(next, 0);
833
			}
834
		}
835
		throw new IllegalStateException("<figure> has no end tag");
836
	}
837

  
838
	/**
839
	 * @param state
840
	 * @param id
841
	 * @param type
842
	 * @param urlString
843
	 * @param legendString
844
	 * @param titleString
845
	 * @param numString
846
	 * @param next
847
	 */
848
	private Media makeFigure(MarkupImportState state, String id, String type, String urlString, 
849
			String legendString, String titleString, String numString, XMLEvent next) {
850
		Media media = null;
851
		boolean isFigure = false;
852
		try {
853
			//TODO maybe everything is a figure as it is all taken from a book
854
			if ("lineart".equals(type)) {
855
				isFigure = true;
856
//				media = Figure.NewInstance(url.toURI(), null, null,	null);
857
			} else if (type == null || "photo".equals(type)
858
					|| "signature".equals(type)
859
					|| "others".equals(type)) {
860
				//TODO
861
			} else {
862
				String message = "Unknown figure type '%s'";
863
				message = String.format(message, type);
864
				fireWarningEvent(message, next, 2);
865
			}
866
			media = docImport.getImageMedia(urlString, docImport.getReadMediaData(), isFigure);
867
			
868
			if (media != null){
869
				// title
870
				if (StringUtils.isNotBlank(titleString)) {
871
					media.putTitle(getDefaultLanguage(state), titleString);
872
				}
873
				// legend
874
				if (StringUtils.isNotBlank(legendString)) {
875
					media.putDescription(getDefaultLanguage(state), legendString);
876
				}
877
				if (StringUtils.isNotBlank(numString)) {
878
					// TODO use concrete source (e.g. DAPHNIPHYLLACEAE in FM
879
					// vol.13)
880
					Reference<?> citation = state.getConfig().getSourceReference();
881
					media.addImportSource(  numString, "num", citation, null);
882
					// TODO name used in source if available
883
				}
884
				// TODO which citation
885
				if (StringUtils.isNotBlank(id)) {
886
					media.addImportSource(id, null, state.getConfig().getSourceReference(), null);
887
				} else {
888
					String message = "Figure id should never be empty or null";
889
					fireWarningEvent(message, next, 6);
890
				}
891

  
892
				// text
893
				// do nothing
894
				registerGivenFigure(state, next, id, media);
895
				
896
			}else{
897
				String message = "No media found: ";
898
				fireWarningEvent(message, next, 4);
899
			}
900
		} catch (MalformedURLException e) {
901
			String message = "Media uri has incorrect syntax: %s";
902
			message = String.format(message, urlString);
903
			fireWarningEvent(message, next, 4);
904
//		} catch (URISyntaxException e) {
905
//			String message = "Media uri has incorrect syntax: %s";
906
//			message = String.format(message, urlString);
907
//			fireWarningEvent(message, next, 4);
908
		}
909

  
910
		return media;
911
	}
912

  
913
	private FigureDataHolder handleFigureRef(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent)
914
			throws XMLStreamException {
915
		FigureDataHolder result = new FigureDataHolder();
916
		Map<String, Attribute> attributes = getAttributes(parentEvent);
917
		result.ref = getAndRemoveAttributeValue(attributes, REF);
918
		checkNoAttributes(attributes, parentEvent);
919

  
920
		// text is not handled, needed only for debugging purposes
921
		String text = "";
922
		while (reader.hasNext()) {
923
			XMLEvent next = readNoWhitespace(reader);
924
			if (isMyEndingElement(next, parentEvent)) {
925
				return result;
926
			} else if (isStartingElement(next, NUM)) {
927
				String num = getCData(state, reader, next);
928
				result.num = num; // num is not handled during import
929
			} else if (isStartingElement(next, FIGURE_PART)) {
930
				result.figurePart = getCData(state, reader, next);
931
			} else if (next.isCharacters()) {
932
				text += next.asCharacters().getData();
933
			} else {
934
				fireUnexpectedEvent(next, 0);
935
			}
936
		}
937
		throw new IllegalStateException("<figureRef> has no end tag");
938
	}
939

  
940
	private FootnoteDataHolder handleFootnote(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
941
		FootnoteDataHolder result = new FootnoteDataHolder();
942
		Map<String, Attribute> attributes = getAttributes(parentEvent);
943
		result.id = getAndRemoveAttributeValue(attributes, ID);
944
		// result.ref = getAndRemoveAttributeValue(attributes, REF);
945
		checkNoAttributes(attributes, parentEvent);
946

  
947
		while (reader.hasNext()) {
948
			XMLEvent next = readNoWhitespace(reader);
949
			if (isStartingElement(next, FOOTNOTE_STRING)) {
950
				String string = handleFootnoteString(state, reader, next);
951
				result.string = string;
952
			} else if (isMyEndingElement(next, parentEvent)) {
953
				return result;
954
			} else {
955
				fireUnexpectedEvent(next, 0);
956
			}
957
		}
958
		return result;
959
	}
960

  
961
	private FootnoteDataHolder handleFootnoteRef(MarkupImportState state,
962
			XMLEventReader reader, XMLEvent parentEvent)
963
			throws XMLStreamException {
964
		FootnoteDataHolder result = new FootnoteDataHolder();
965
		Map<String, Attribute> attributes = getAttributes(parentEvent);
966
		result.ref = getAndRemoveAttributeValue(attributes, REF);
967
		checkNoAttributes(attributes, parentEvent);
968

  
969
		// text is not handled, needed only for debugging purposes
970
		String text = "";
971
		while (reader.hasNext()) {
972
			XMLEvent next = readNoWhitespace(reader);
973
			// if (isStartingElement(next, FOOTNOTE_STRING)){
974
			// String string = handleFootnoteString(state, reader, next);
975
			// result.string = string;
976
			// }else
977
			if (isMyEndingElement(next, parentEvent)) {
978
				return result;
979
			} else if (next.isCharacters()) {
980
				text += next.asCharacters().getData();
981

  
982
			} else {
983
				fireUnexpectedEvent(next, 0);
984
			}
985
		}
986
		return result;
987
	}
988

  
989

  
990

  
991
	private String handleFootnoteString(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
992
		boolean isTextMode = true;
993
		String text = "";
994
		while (reader.hasNext()) {
995
			XMLEvent next = readNoWhitespace(reader);
996
			if (isMyEndingElement(next, parentEvent)) {
997
				return text;
998
			} else if (next.isEndElement()) {
999
				if (isEndingElement(next, FULL_NAME)) {
1000
					popUnimplemented(next.asEndElement());
1001
				} else if (isEndingElement(next, BR)) {
1002
					isTextMode = true;
1003
				} else if (isHtml(next)) {
1004
					text += getXmlTag(next);
1005
				} else {
1006
					handleUnexpectedEndElement(next.asEndElement());
1007
				}
1008
			} else if (next.isStartElement()) {
1009
				if (isStartingElement(next, FULL_NAME)) {
1010
					handleNotYetImplementedElement(next);
1011
				} else if (isStartingElement(next, GATHERING)) {
1012
					text += specimenImport.handleInLineGathering(state, reader, next);
1013
				} else if (isStartingElement(next, REFERENCES)) {
1014
					text += " " + handleInLineReferences(state, reader, next)+ " ";
1015
				} else if (isStartingElement(next, BR)) {
1016
					text += "<br/>";
1017
					isTextMode = false;
1018
				} else if (isStartingElement(next, NOMENCLATURE)) {
1019
					handleNotYetImplementedElement(next);
1020
				} else if (isHtml(next)) {
1021
					text += getXmlTag(next);
1022
				} else {
1023
					handleUnexpectedStartElement(next.asStartElement());
1024
				}
1025
			} else if (next.isCharacters()) {
1026
				if (!isTextMode) {
1027
					String message = "footnoteString is not in text mode";
1028
					fireWarningEvent(message, next, 6);
1029
				} else {
1030
					text += next.asCharacters().getData().trim(); 
1031
					// getCData(state, reader, next); does not work as we have inner tags like <references>
1032
				}
1033
			} else {
1034
				handleUnexpectedEndElement(next.asEndElement());
1035
			}
1036
		}
1037
		throw new IllegalStateException("<footnoteString> has no closing tag");
1038

  
1039
	}
1040

  
1041
	private String handleInLineReferences(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
1042
		checkNoAttributes(parentEvent);
1043

  
1044
		boolean hasReference = false;
1045
		String text = "";
1046
		while (reader.hasNext()) {
1047
			XMLEvent next = readNoWhitespace(reader);
1048
			if (isMyEndingElement(next, parentEvent)) {
1049
				checkMandatoryElement(hasReference, parentEvent.asStartElement(), REFERENCE);
1050
				return text;
1051
			} else if (isStartingElement(next, REFERENCE)) {
1052
				text += handleInLineReference(state, reader, next);
1053
				hasReference = true;
1054
			} else {
1055
				handleUnexpectedElement(next);
1056
			}
1057
		}
1058
		throw new IllegalStateException("<References> has no closing tag");
1059
	}
1060

  
1061
	private String handleInLineReference(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent)throws XMLStreamException {
1062
		Reference<?> reference = nomenclatureImport.handleReference(state, reader, parentEvent);
1063
		String result = "<cdm:ref uuid='%s'>%s</ref>";
1064
		result = String.format(result, reference.getUuid(), reference.getTitleCache());
1065
		save(reference, state);
1066
		return result;
1067
	}
1068

  
1069
	private void handleFeature(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
1070
		Map<String, Attribute> attrs = getAttributes(parentEvent);
1071
		Boolean isFreetext = getAndRemoveBooleanAttributeValue(parentEvent, attrs, IS_FREETEXT, false);
1072
		String classValue =getAndRemoveRequiredAttributeValue(parentEvent, attrs, CLASS);
1073
		checkNoAttributes(attrs, parentEvent);
1074
		
1075
		
1076
		Feature feature = makeFeature(classValue, state, parentEvent, null);
1077
		Taxon taxon = state.getCurrentTaxon();
1078
		TaxonDescription taxonDescription = getTaxonDescription(taxon, state.getConfig().getSourceReference(), NO_IMAGE_GALLERY, CREATE_NEW);
1079
		// TextData figureHolderTextData = null; //for use with one TextData for
1080
		// all figure only
1081

  
1082
		boolean isDescription = feature.equals(Feature.DESCRIPTION());
1083
		DescriptionElementBase lastDescriptionElement = null;
1084
		
1085
		while (reader.hasNext()) {
1086
			XMLEvent next = readNoWhitespace(reader);
1087
			if (isMyEndingElement(next, parentEvent)) {
1088
				state.putFeatureToGeneralSorterList(feature);
1089
				return;
1090
			} else if (isEndingElement(next, DISTRIBUTION_LIST) || isEndingElement(next, HABITAT_LIST)) { 
1091
				// only handle list elements
1092
			} else if (isStartingElement(next, HEADING)) {
1093
				makeFeatureHeading(state, reader, classValue, feature, next);
1094
			} else if (isStartingElement(next, WRITER)) {
1095
				makeFeatureWriter(state, reader, feature, taxon, next);
1096
//			} else if (isStartingElement(next, DISTRIBUTION_LOCALITY)) {
1097
//				if (!feature.equals(Feature.DISTRIBUTION())) {
1098
//					String message = "Distribution locality only allowed for feature of type 'distribution'";
1099
//					fireWarningEvent(message, next, 4);
1100
//				}
1101
//				handleDistributionLocality(state, reader, next);
1102
			} else if (isStartingElement(next, DISTRIBUTION_LIST) || isStartingElement(next, HABITAT_LIST)) {
1103
				// only handle single list elements
1104
			} else if (isStartingElement(next, HABITAT)) {
1105
				if (!(feature.equals(Feature.HABITAT())
1106
						|| feature.equals(Feature.HABITAT_ECOLOGY()) 
1107
						|| feature.equals(Feature.ECOLOGY()))) {
1108
					String message = "Habitat only allowed for feature of type 'habitat','habitat ecology' or 'ecology'";
1109
					fireWarningEvent(message, next, 4);
1110
				}
1111
				handleHabitat(state, reader, next);
1112
			} else if (isStartingElement(next, CHAR)) {
1113
				List<TextData> textDataList = handleChar(state, reader, next, null);
1114
				for (TextData textData : textDataList){
1115
					taxonDescription.addElement(textData);
1116
				}
1117
			} else if (isStartingElement(next, STRING)) {
1118
				lastDescriptionElement = makeFeatureString(state, reader,feature, taxonDescription, lastDescriptionElement,next, isFreetext);
1119
			} else if (isStartingElement(next, FIGURE_REF)) {
1120
				lastDescriptionElement = makeFeatureFigureRef(state, reader, taxonDescription, isDescription, lastDescriptionElement, next);
1121
			} else if (isStartingElement(next, REFERENCES)) {
1122
				// TODO details/microcitation ??
1123

  
1124
				List<Reference<?>> refs = handleReferences(state, reader, next);
1125
				if (!refs.isEmpty()) {
1126
					// TODO
1127
					Reference<?> descriptionRef = state.getConfig().getSourceReference();
1128
					TaxonDescription description = getTaxonDescription(taxon, descriptionRef, false, true);
1129
					TextData featurePlaceholder = docImport.getFeaturePlaceholder(state, description, feature, true);
1130
					for (Reference<?> citation : refs) {
1131
						featurePlaceholder.addSource(OriginalSourceType.PrimaryTaxonomicSource,
1132
								null, null, citation, null);
1133
					}
1134
				} else {
1135
					String message = "No reference found in references";
1136
					fireWarningEvent(message, next, 6);
1137
				}
1138
			} else if (isStartingElement(next, NUM)) {
1139
				//TODO
1140
				handleNotYetImplementedElement(next);
1141
			} else if (isEndingElement(next, NUM)) {
1142
				//TODO
1143
				popUnimplemented(next.asEndElement());
1144
			} else {
1145
				handleUnexpectedElement(next);
1146
			}
1147
		}
1148
		throw new IllegalStateException("<Feature> has no closing tag");
1149
	}
1150

  
1151
	/**
1152
	 * @param state
1153
	 * @param reader
1154
	 * @param taxonDescription
1155
	 * @param isDescription
1156
	 * @param lastDescriptionElement
1157
	 * @param next
1158
	 * @return
1159
	 * @throws XMLStreamException
1160
	 */
1161
	private DescriptionElementBase makeFeatureFigureRef(MarkupImportState state, XMLEventReader reader,TaxonDescription taxonDescription, 
1162
					boolean isDescription, DescriptionElementBase lastDescriptionElement, XMLEvent next) throws XMLStreamException {
1163
		FigureDataHolder figureHolder = handleFigureRef(state, reader, next);
1164
		Feature figureFeature = getFeature(state, MarkupTransformer.uuidFigures, "Figures", "Figures", "Fig.",null);
1165
		if (isDescription) {
1166
			TextData figureHolderTextData = null;
1167
			// if (figureHolderTextData == null){
1168
			figureHolderTextData = TextData.NewInstance(figureFeature);
1169
			if (StringUtils.isNotBlank(figureHolder.num)) {
1170
				String annotationText = "<num>" + figureHolder.num.trim() + "</num>";
1171
				Annotation annotation = Annotation.NewInstance(annotationText, AnnotationType.TECHNICAL(), getDefaultLanguage(state));
1172
				figureHolderTextData.addAnnotation(annotation);
1173
			}
1174
			if (StringUtils.isNotBlank(figureHolder.figurePart)) {
1175
				String annotationText = "<figurePart>"+ figureHolder.figurePart.trim() + "</figurePart>";
1176
				Annotation annotation = Annotation.NewInstance(annotationText,AnnotationType.EDITORIAL(), getDefaultLanguage(state));
1177
				figureHolderTextData.addAnnotation(annotation);
1178
			}
1179
			// if (StringUtils.isNotBlank(figureText)){
1180
			// figureHolderTextData.putText(language, figureText);
1181
			// }
1182
			taxonDescription.addElement(figureHolderTextData);
1183
			// }
1184
			registerFigureDemand(state, next, figureHolderTextData, figureHolder.ref);
1185
		} else {
1186
			if (lastDescriptionElement == null) {
1187
				String message = "No description element created yet that can be referred by figure. Create new TextData instead";
1188
				fireWarningEvent(message, next, 4);
1189
				lastDescriptionElement = TextData.NewInstance(figureFeature);
1190
				taxonDescription.addElement(lastDescriptionElement);
1191
			}
1192
			registerFigureDemand(state, next, lastDescriptionElement,	figureHolder.ref);
1193
		}
1194
		return lastDescriptionElement;
1195
	}
1196

  
1197
	/**
1198
	 * @param state
1199
	 * @param reader
1200
	 * @param feature
1201
	 * @param taxonDescription
1202
	 * @param lastDescriptionElement
1203
	 * @param distributionList 
1204
	 * @param next
1205
	 * @return
1206
	 * @throws XMLStreamException
1207
	 */
1208
	private DescriptionElementBase makeFeatureString(MarkupImportState state,XMLEventReader reader, Feature feature, 
1209
				TaxonDescription taxonDescription, DescriptionElementBase lastDescriptionElement, XMLEvent next, Boolean isFreetext) throws XMLStreamException {
1210
		
1211
		//for specimen only
1212
		if (feature.equals(Feature.SPECIMEN()) || feature.equals(Feature.MATERIALS_EXAMINED())){
1213
			
1214
			List<DescriptionElementBase> specimens = specimenImport.handleMaterialsExamined(state, reader, next, feature);
1215
			for (DescriptionElementBase specimen : specimens){
1216
				taxonDescription.addElement(specimen);
1217
				lastDescriptionElement = specimen;
1218
			}
1219
			state.setCurrentCollector(null);
1220
			
1221
			return lastDescriptionElement;
1222
		}else{
1223
		
1224
			//others
1225
			Map<String, String> subheadingMap = handleString(state, reader, next, feature);
1226
			for (String subheading : subheadingMap.keySet()) {
1227
				Feature subheadingFeature = feature;
1228
				if (StringUtils.isNotBlank(subheading) && subheadingMap.size() > 1) {
1229
					subheadingFeature = makeFeature(subheading, state, next, null);
1230
				}
1231
				if (feature.equals(Feature.COMMON_NAME()) && (isFreetext == null || !isFreetext)){
1232
					List<DescriptionElementBase> commonNames = makeVernacular(state, subheading, subheadingMap.get(subheading));
1233
					for (DescriptionElementBase commonName : commonNames){
1234
						taxonDescription.addElement(commonName);
1235
						lastDescriptionElement = commonName;
1236
					}
1237
				}else {
1238
					TextData textData = TextData.NewInstance(subheadingFeature);
1239
					textData.putText(getDefaultLanguage(state), subheadingMap.get(subheading));
1240
					taxonDescription.addElement(textData);
1241
					lastDescriptionElement = textData;
1242
					// TODO how to handle figures when these data are split in
1243
					// subheadings
1244
				}
1245
			}
1246
			return lastDescriptionElement;
1247
		}
1248
	}
1249

  
1250
	private List<DescriptionElementBase> makeVernacular(MarkupImportState state, String subheading, String commonNameString) throws XMLStreamException {
1251
		List<DescriptionElementBase> result = new ArrayList<DescriptionElementBase>();
1252
		String[] splits = commonNameString.split(",");
1253
		for (String split : splits){
1254
			split = split.trim();
1255
			if (! split.matches(".*\\(.*\\)\\.?")){
1256
				fireWarningEvent("Common name string '"+split+"' does not match given pattern", state.getReader().peek(), 4);
1257
			}
1258
			
1259
			String name = split.replaceAll("\\(.*\\)", "").replace(".", "").trim();
1260
			String languageStr = split.replaceFirst(".*\\(", "").replaceAll("\\)\\.?", "").trim();
1261
			
1262
			Language language = null;
1263
			if (StringUtils.isNotBlank(languageStr)){
1264
				try {
1265
					UUID langUuid = state.getTransformer().getLanguageUuid(languageStr);
1266
					TermVocabulary<?> voc = null;
1267
					language = getLanguage(state, langUuid, languageStr, languageStr, null, voc);
1268
					if (language == null){
1269
						logger.warn("Language " + languageStr + " not recognized by transformer");
1270
					}
1271
				} catch (UndefinedTransformerMethodException e) {
1272
					throw new RuntimeException(e);
1273
				}
1274
			}
1275
			NamedArea area = null;
1276
			CommonTaxonName commonTaxonName = CommonTaxonName.NewInstance(name, language, area);
1277
			result.add(commonTaxonName);
1278
		}
1279
		
1280
		return result;
1281
	}
1282

  
1283
	/**
1284
	 * @param state
1285
	 * @param reader
1286
	 * @param feature
1287
	 * @param taxon
1288
	 * @param next
1289
	 * @throws XMLStreamException
1290
	 */
1291
	private void makeFeatureWriter(MarkupImportState state,XMLEventReader reader, Feature feature, Taxon taxon, XMLEvent next) throws XMLStreamException {
1292
		WriterDataHolder writer = handleWriter(state, reader, next);
1293
		if (isNotBlank(writer.writer)) {
1294
			// TODO
1295
			Reference<?> ref = state.getConfig().getSourceReference();
1296
			TaxonDescription description = getTaxonDescription(taxon, ref,
1297
					false, true);
1298
			TextData featurePlaceholder = docImport.getFeaturePlaceholder(state,
1299
					description, feature, true);
1300
			featurePlaceholder.addAnnotation(writer.annotation);
1301
			registerFootnotes(state, featurePlaceholder, writer.footnotes);
1302
		} else {
1303
			String message = "Writer element is empty";
1304
			fireWarningEvent(message, next, 4);
1305
		}
1306
	}
1307

  
1308
	/**
1309
	 * @param state
1310
	 * @param reader
1311
	 * @param classValue
1312
	 * @param feature
1313
	 * @param next
1314
	 * @throws XMLStreamException
1315
	 */
1316
	private void makeFeatureHeading(MarkupImportState state, XMLEventReader reader, String classValue, Feature feature, XMLEvent next) throws XMLStreamException {
1317
		String heading = handleHeading(state, reader, next);
1318
		if (StringUtils.isNotBlank(heading)) {
1319
			if (!heading.equalsIgnoreCase(classValue)) {
1320
				try {
1321
					if (!feature.equals(state.getTransformer().getFeatureByKey(
1322
							heading))) {
1323
						UUID headerFeatureUuid = state.getTransformer()
1324
								.getFeatureUuid(heading);
1325
						if (!feature.getUuid().equals(headerFeatureUuid)) {
1326
							String message = "Feature heading '%s' differs from feature class '%s' and can not be transformed to feature";
1327
							message = String.format(message, heading,
1328
									classValue);
1329
							fireWarningEvent(message, next, 1);
1330
						}
1331
					}
1332
				} catch (UndefinedTransformerMethodException e) {
1333
					throw new RuntimeException(e);
1334
				}
1335
			} else {
1336
				// do nothing
1337
			}
1338
		}
1339
	}
1340

  
1341
	private List<Reference<?>> handleReferences(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
1342
		// attributes
1343
		Map<String, Attribute> attributes = getAttributes(parentEvent);
1344
		String bibliography = getAndRemoveAttributeValue(attributes,
1345
				BIBLIOGRAPHY);
1346
		String serialsAbbreviations = getAndRemoveAttributeValue(attributes,
1347
				SERIALS_ABBREVIATIONS);
1348
		if (isNotBlank(bibliography) || isNotBlank(serialsAbbreviations)) {
1349
			String message = "Attributes not yet implemented for <references>";
1350
			fireWarningEvent(message, parentEvent, 4);
1351
		}
1352

  
1353
		List<Reference<?>> result = new ArrayList<Reference<?>>();
1354

  
1355
		// elements
1356
		while (reader.hasNext()) {
1357
			XMLEvent next = readNoWhitespace(reader);
1358
			if (next.isEndElement()) {
1359
				if (isMyEndingElement(next, parentEvent)) {
1360
					return result;
1361
				} else {
1362
					if (isEndingElement(next, HEADING)) {
1363
						// NOT YET IMPLEMENTED
1364
						popUnimplemented(next.asEndElement());
1365
					} else if (isEndingElement(next, WRITER)) {
1366
						// NOT YET IMPLEMENTED
1367
						popUnimplemented(next.asEndElement());
1368
					} else if (isEndingElement(next, FOOTNOTE)) {
1369
						// NOT YET IMPLEMENTED
1370
						popUnimplemented(next.asEndElement());
1371
					} else if (isEndingElement(next, STRING)) {
1372
						// NOT YET IMPLEMENTED
1373
						popUnimplemented(next.asEndElement());
1374
					} else if (isEndingElement(next, REF_NUM)) {
1375
						// NOT YET IMPLEMENTED
1376
						popUnimplemented(next.asEndElement());
1377
					} else {
1378
						handleUnexpectedEndElement(next.asEndElement());
1379
					}
1380
				}
1381
			} else if (next.isStartElement()) {
1382
				if (isStartingElement(next, HEADING)) {
1383
					handleNotYetImplementedElement(next);
1384
				} else if (isStartingElement(next, SUB_HEADING)) {
1385
					String subheading = getCData(state, reader, next).trim();
1386
					String excludePattern = "(i?)(References?|Literature):?";
1387
					if (!subheading.matches(excludePattern)) {
1388
						fireNotYetImplementedElement(next.getLocation(), next.asStartElement().getName(), 0);
1389
					}
1390
				} else if (isStartingElement(next, WRITER)) {
1391
					handleNotYetImplementedElement(next);
1392
				} else if (isStartingElement(next, FOOTNOTE)) {
1393
					handleNotYetImplementedElement(next);
1394
				} else if (isStartingElement(next, STRING)) {
1395
					handleNotYetImplementedElement(next);
1396
				} else if (isStartingElement(next, REF_NUM)) {
1397
					handleNotYetImplementedElement(next);
1398
				} else if (isStartingElement(next, REFERENCE)) {
1399
					Reference<?> ref = nomenclatureImport.handleReference(state, reader, next);
1400
					result.add(ref);
1401
				} else {
1402
					handleUnexpectedStartElement(next);
1403
				}
1404
			} else {
1405
				handleUnexpectedElement(next);
1406
			}
1407
		}
1408
		throw new IllegalStateException("<References> has no closing tag");
1409
	}
1410

  
1411
	private void handleHabitat(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
1412
		checkNoAttributes(parentEvent);
1413
		Taxon taxon = state.getCurrentTaxon();
1414
		// TODO which ref to take?
1415
		Reference<?> ref = state.getConfig().getSourceReference();
1416

  
1417
		String text = "";
1418
		while (reader.hasNext()) {
1419
			XMLEvent next = readNoWhitespace(reader);
1420
			if (isMyEndingElement(next, parentEvent)) {
1421
				TaxonDescription description = getTaxonDescription(taxon, ref,
1422
						false, true);
1423
				UUID uuidExtractedHabitat = MarkupTransformer.uuidExtractedHabitat;
1424
				Feature feature = getFeature(
1425
						state,
1426
						uuidExtractedHabitat,
1427
						"Extracted Habitat",
1428
						"An structured habitat that was extracted from a habitat text",
1429
						"extr. habit.", null);
1430
				TextData habitat = TextData.NewInstance(feature);
1431
				habitat.putText(getDefaultLanguage(state), text);
1432
				description.addElement(habitat);
1433

  
1434
				return;
1435
			} else if (next.isStartElement()) {
1436
				if (isStartingElement(next, ALTITUDE)) {
1437
					text = text.trim() + getTaggedCData(state, reader, next);
1438
				} else if (isStartingElement(next, LIFE_CYCLE_PERIODS)) {
1439
					handleNotYetImplementedElement(next);
1440
				} else {
1441
					handleUnexpectedStartElement(next.asStartElement());
1442
				}
1443
			} else if (next.isCharacters()) {
1444
				text += next.asCharacters().getData();
1445
			} else {
1446
				handleUnexpectedElement(next);
1447
			}
1448
		}
1449
		throw new IllegalStateException("<Habitat> has no closing tag");
1450
	}
1451

  
1452
	private String getTaggedCData(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
1453
		checkNoAttributes(parentEvent);
1454

  
1455
		String text = getXmlTag(parentEvent);
1456
		while (reader.hasNext()) {
1457
			XMLEvent next = readNoWhitespace(reader);
1458
			if (isMyEndingElement(next, parentEvent)) {
1459
				text += getXmlTag(next);
1460
				return text;
1461
			} else if (next.isStartElement()) {
1462
				text += getTaggedCData(state, reader, next);
1463
			} else if (next.isEndElement()) {
1464
				text += getTaggedCData(state, reader, next);
1465
			} else if (next.isCharacters()) {
1466
				text += next.asCharacters().getData();
1467
			} else {
1468
				handleUnexpectedEndElement(next.asEndElement());
1469
			}
1470
		}
1471
		throw new IllegalStateException("Some tag has no closing tag");
1472
	}
1473

  
1474
	private String handleDistributionLocality(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent)throws XMLStreamException {
1475
		Map<String, Attribute> attributes = getAttributes(parentEvent);
1476
		String classValue = getAndRemoveRequiredAttributeValue(parentEvent, attributes, CLASS);
1477
		String statusValue =getAndRemoveAttributeValue(attributes, STATUS);
1478
		String frequencyValue =getAndRemoveAttributeValue(attributes, FREQUENCY);
1479
		
1480

  
1481
		Taxon taxon = state.getCurrentTaxon();
1482
		// TODO which ref to take?
1483
		Reference<?> ref = state.getConfig().getSourceReference();
1484

  
1485
		String text = "";
1486
		while (reader.hasNext()) {
1487
			XMLEvent next = readNoWhitespace(reader);
1488
			if (isMyEndingElement(next, parentEvent)) {
1489
				if (StringUtils.isNotBlank(text)) {
1490
					String label = CdmUtils.removeTrailingDot(normalize(text));
1491
					TaxonDescription description = getTaxonDescription(taxon, ref, false, true);
1492
					NamedAreaLevel level = makeNamedAreaLevel(state,classValue, next);
1493
					
1494
					//status
1495
					PresenceAbsenceTermBase<?> status = null;
1496
					if (isNotBlank(statusValue)){
1497
						try {
1498
							status = state.getTransformer().getPresenceTermByKey(statusValue);
1499
							if (status == null){
1500
								//TODO
1501
								String message = "The presence/absence status '%s' could not be transformed to an CDM status";								
1502
								fireWarningEvent(String.format(message, statusValue), next, 4);
1503
							}
1504
						} catch (UndefinedTransformerMethodException e) {
1505
							throw new RuntimeException(e);
1506
						}
1507
					}else{
1508
						status = PresenceTerm.PRESENT();
1509
					}
1510
					//frequency
1511
					if (isNotBlank(frequencyValue)){
1512
						String message = "The frequency attribute is currently not yet available in CDM";
1513
						fireWarningEvent(message, parentEvent, 6);
1514
					}
1515
					
1516
					NamedArea higherArea = null;
1517
					List<NamedArea> areas = new ArrayList<NamedArea>(); 
1518
					
1519
					String patSingleArea = "([^,\\(]{3,})";
1520
					String patSeparator = "(,|\\sand\\s)";
1521
					String hierarchiePattern = String.format("%s\\((%s(%s%s)*)\\)",patSingleArea, patSingleArea, patSeparator, patSingleArea);
1522
					Pattern patHierarchie = Pattern.compile(hierarchiePattern, Pattern.CASE_INSENSITIVE);
1523
					Matcher matcher = patHierarchie.matcher(label); 
1524
					if (matcher.matches()){
1525
						String higherAreaStr = matcher.group(1).trim();
1526
						higherArea =  makeArea(state, higherAreaStr, level);
1527
						String[] innerAreas = matcher.group(2).split(patSeparator);
1528
						for (String innerArea : innerAreas){
1529
							if (isNotBlank(innerArea)){
1530
								NamedArea singleArea = makeArea(state, innerArea.trim(), level);
1531
								areas.add(singleArea);
1532
								NamedArea partOf = singleArea.getPartOf();
1533
//								if (partOf == null){
1534
//									singleArea.setPartOf(higherArea);
1535
//								}
1536
							}
1537
						}
1538
					}else{
1539
						NamedArea singleArea = makeArea(state, label, level);
1540
						areas.add(singleArea);
1541
					}
1542
					
1543
					for (NamedArea area : areas){
1544
						//create distribution
1545
						Distribution distribution = Distribution.NewInstance(area,status);
1546
						description.addElement(distribution);
1547
					}
1548
				} else {
1549
					String message = "Empty distribution locality";
1550
					fireWarningEvent(message, next, 4);
1551
				}
1552
				return text;
1553
			} else if (isStartingElement(next, COORDINATES)) {
1554
				//TODO
1555
				handleNotYetImplementedElement(next);
1556
			} else if (isEndingElement(next, COORDINATES)) {
1557
				//TODO
1558
				popUnimplemented(next.asEndElement());
1559
			} else if (next.isCharacters()) {
1560
				text += next.asCharacters().getData();
1561
			} else {
1562
				handleUnexpectedElement(next);
1563
			}
1564
		}
1565
		throw new IllegalStateException("<DistributionLocality> has no closing tag");
1566
	}	
1567

  
1568
	private String handleHeading(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent)throws XMLStreamException {
1569
		checkNoAttributes(parentEvent);
1570

  
1571
		String text = "";
1572
		while (reader.hasNext()) {
1573
			XMLEvent next = readNoWhitespace(reader);
1574
			if (isMyEndingElement(next, parentEvent)) {
1575
				return text;
1576
			} else if (next.isStartElement()) {
1577
				if (isStartingElement(next, FOOTNOTE)) {
1578
					handleNotYetImplementedElement(next);
1579
				} else {
1580
					handleUnexpectedStartElement(next.asStartElement());
1581
				}
1582
			} else if (next.isCharacters()) {
1583
				text += next.asCharacters().getData();
1584
			} else {
1585
				handleUnexpectedEndElement(next.asEndElement());
1586
			}
1587
		}
1588
		throw new IllegalStateException("<String> has no closing tag");
1589

  
1590
	}
1591

  
1592
	/**
1593
	 * Handle string
1594
	 * @param state
1595
	 * @param reader
1596
	 * @param parentEvent
1597
	 * @param feature only needed for distributionLocalities
1598
	 * @return
1599
	 * @throws XMLStreamException
1600
	 */
1601
	private Map<String, String> handleString(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, Feature feature)throws XMLStreamException {
1602
		// attributes
1603
		String classValue = getClassOnlyAttribute(parentEvent, false);
1604
		if (StringUtils.isNotBlank(classValue)) {
1605
			String message = "class attribute for <string> not yet implemented";
1606
			fireWarningEvent(message, parentEvent, 2);
1607
		}
1608

  
1609
		// subheadings
1610
		Map<String, String> subHeadingMap = new HashMap<String, String>();
1611
		String currentSubheading = null;
1612

  
1613
		boolean isTextMode = true;
1614
		String text = "";
1615
		while (reader.hasNext()) {
1616
			XMLEvent next = readNoWhitespace(reader);
1617
			if (isMyEndingElement(next, parentEvent)) {
1618
				putCurrentSubheading(subHeadingMap, currentSubheading, text);
1619
				return subHeadingMap;
1620
			} else if (isStartingElement(next, BR)) {
1621
				text += "<br/>";
1622
				isTextMode = false;
1623
			} else if (isEndingElement(next, BR)) {
1624
				isTextMode = true;
1625
			} else if (isHtml(next)) {
1626
				text += getXmlTag(next);
1627
			} else if (isStartingElement(next, SUB_HEADING)) {
1628
				text = putCurrentSubheading(subHeadingMap,currentSubheading, text);
1629
				// TODO footnotes
1630
				currentSubheading = getCData(state, reader, next).trim();
1631
			} else if (isStartingElement(next, DISTRIBUTION_LOCALITY)) {
1632
				if (feature != null && !feature.equals(Feature.DISTRIBUTION())) {
1633
					String message = "Distribution locality only allowed for feature of type 'distribution'";
1634
					fireWarningEvent(message, next, 4);
1635
				}
1636
				text += handleDistributionLocality(state, reader, next);
1637
			} else if (next.isCharacters()) {
1638
				if (! isTextMode) {
1639
					String message = "String is not in text mode";
1640
					fireWarningEvent(message, next, 6);
1641
				} else {
1642
					text += next.asCharacters().getData();
1643
				}
1644
			} else if (isStartingElement(next, HEADING)) {
1645
				//TODO
1646
				handleNotYetImplementedElement(next);
1647
			} else if (isStartingElement(next, VERNACULAR_NAMES)) {
1648
				//TODO
1649
				handleNotYetImplementedElement(next);
1650
			} else if (isEndingElement(next, HEADING)) {
1651
				//TODO
1652
				popUnimplemented(next.asEndElement());
1653
			} else if (isStartingElement(next, QUOTE)) {
1654
				//TODO
1655
				handleNotYetImplementedElement(next);
1656
			} else if (isEndingElement(next, QUOTE)) {
1657
				//TODO
1658
				popUnimplemented(next.asEndElement());
1659
			} else if (isStartingElement(next, DEDICATION)) {
1660
				//TODO
1661
				handleNotYetImplementedElement(next);
1662
			} else if (isEndingElement(next, DEDICATION)) {
1663
				//TODO
1664
				popUnimplemented(next.asEndElement());
1665
			} else if (isStartingElement(next, TAXONTYPE)) {
1666
				//TODO
1667
				handleNotYetImplementedElement(next);
1668
			} else if (isEndingElement(next, TAXONTYPE)) {
1669
				//TODO
1670
				popUnimplemented(next.asEndElement());
1671
			} else if (isStartingElement(next, FULL_NAME)) {
1672
				//TODO
1673
				handleNotYetImplementedElement(next);
1674
			} else if (isEndingElement(next, FULL_NAME)) {
1675
				//TODO
1676
				popUnimplemented(next.asEndElement());
1677
			}else if (isStartingElement(next, REFERENCES)) {
1678
				//TODO
1679
				handleNotYetImplementedElement(next);
1680
			} else if (isEndingElement(next, REFERENCES)) {
1681
				//TODO
1682
				popUnimplemented(next.asEndElement());
1683
			} else if (isStartingElement(next, GATHERING)) {
1684
				//TODO
1685
				handleNotYetImplementedElement(next);
1686
			} else if (isEndingElement(next, GATHERING)) {
1687
				//TODO
1688
				popUnimplemented(next.asEndElement());
1689
			} else if (isStartingElement(next, ANNOTATION)) {
1690
				//TODO  //TODO test handleSimpleAnnotation
1691
				handleNotYetImplementedElement(next);
1692
			} else if (isEndingElement(next, ANNOTATION)) {
1693
				//TODO
1694
				popUnimplemented(next.asEndElement());
1695
			} else if (isStartingElement(next, HABITAT)) {
1696
				//TODO
1697
				handleNotYetImplementedElement(next);
1698
			} else if (isEndingElement(next, HABITAT)) {
1699
				//TODO
1700
				popUnimplemented(next.asEndElement());
1701
			} else if (isStartingElement(next, FIGURE_REF)) {
1702
				//TODO
1703
				handleNotYetImplementedElement(next);
1704
			} else if (isEndingElement(next, FIGURE_REF)) {
1705
				//TODO
1706
				popUnimplemented(next.asEndElement());
1707
			} else if (isStartingElement(next, FIGURE)) {
1708
				//TODO
1709
				handleNotYetImplementedElement(next);
1710
			} else if (isEndingElement(next, FIGURE)) {
1711
				//TODO
1712
				popUnimplemented(next.asEndElement());
1713
			} else if (isStartingElement(next, FOOTNOTE_REF)) {
1714
				//TODO
1715
				handleNotYetImplementedElement(next);
1716
			} else if (isEndingElement(next, FOOTNOTE_REF)) {
1717
				//TODO
1718
				popUnimplemented(next.asEndElement());
1719
			} else if (isStartingElement(next, FOOTNOTE)) {
1720
				//TODO
1721
				handleNotYetImplementedElement(next);
1722
			} else if (isEndingElement(next, FOOTNOTE)) {
1723
				//TODO
1724
				popUnimplemented(next.asEndElement());
1725
			} else if (isStartingElement(next, WRITER)) {
1726
				//TODO
1727
				handleNotYetImplementedElement(next);
1728
			} else if (isEndingElement(next, WRITER)) {
1729
				//TODO
1730
				popUnimplemented(next.asEndElement());
1731
			} else if (isStartingElement(next, DATES)) {
1732
				//TODO
1733
				handleNotYetImplementedElement(next);
1734
			} else if (isEndingElement(next, DATES)) {
1735
				//TODO
1736
				popUnimplemented(next.asEndElement());
1737
			} else {
1738
				handleUnexpectedElement(next);
1739
			}
1740
		}
1741
		throw new IllegalStateException("<String> has no closing tag");
1742
	}
1743

  
1744
	/**
1745
	 * @param subHeadingMap
1746
	 * @param currentSubheading
1747
	 * @param text
1748
	 * @return
1749
	 */
1750
	private String putCurrentSubheading(Map<String, String> subHeadingMap, String currentSubheading, String text) {
1751
		if (StringUtils.isNotBlank(text)) {
1752
			text = removeStartingMinus(text);
1753
			subHeadingMap.put(currentSubheading, text.trim());
1754
		}
1755
		return "";
1756
	}
1757

  
1758
	private String removeStartingMinus(String string) {
1759
		string = replaceStart(string, "-");
1760
		string = replaceStart(string, "\u002d");
1761
		string = replaceStart(string, "\u2013");
1762
		string = replaceStart(string, "\u2014");
1763
		string = replaceStart(string, "--");
1764
		return string;
1765
	}
1766
	
1767
	/**
1768
	 * @param value
1769
	 * @param replacementString
1770
	 */
1771
	private String replaceStart(String value, String replacementString) {
1772
		if (value.startsWith(replacementString) ){
1773
			value = value.substring(replacementString.length()).trim();
1774
		}
1775
		while (value.startsWith("-") || value.startsWith("\u2014") ){
1776
			value = value.substring("-".length()).trim();
1777
		}
1778
		return value;
1779
	}
1780
	
1781
	private String getXmlTag(XMLEvent event) {
1782
		String result;
1783
		if (event.isStartElement()) {
1784
			result = "<" + event.asStartElement().getName().getLocalPart()
1785
					+ ">";
1786
		} else if (event.isEndElement()) {
1787
			result = "</" + event.asEndElement().getName().getLocalPart() + ">";
1788
		} else {
1789
			String message = "Only start or end elements are allowed as Html tags";
1790
			throw new IllegalStateException(message);
1791
		}
1792
		return result;
1793
	}
1794

  
1795
	protected static final List<String> htmlList = Arrays.asList("sub", "sup",
1796
			"ol", "ul", "li", "i", "b", "table", "br","tr","td");
1797

  
1798
	private boolean isHtml(XMLEvent event) {
1799
		if (event.isStartElement()) {
1800
			String tag = event.asStartElement().getName().getLocalPart();
1801
			return htmlList.contains(tag);
1802
		} else if (event.isEndElement()) {
1803
			String tag = event.asEndElement().getName().getLocalPart();
1804
			return htmlList.contains(tag);
1805
		} else {
1806
			return false;
1807
		}
1808

  
1809
	}
1810

  
1811
	/**
1812
	 * Handle the char or subchar element. As 
1813
	 * @param state the import state
1814
	 * @param reader 
1815
	 * @param parentEvent
1816
	 * @param parentFeature in case of subchars we need to attache the newly created feature to a parent feature, should be <code>null</code>
1817
	 * for top level chars.  
1818
	 * @return List of TextData. Not a single one as the recursive TextData will also be returned
1819
	 * @throws XMLStreamException
1820
	 */
1821
	private List<TextData> handleChar(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, Feature parentFeature) throws XMLStreamException {
1822
		List<TextData> result = new ArrayList<TextData>();
1823
		String classValue = getClassOnlyAttribute(parentEvent);
1824
		Feature feature = makeFeature(classValue, state, parentEvent, parentFeature);
1825

  
1826
		boolean isTextMode = true;
1827
		String text = "";
1828
		while (reader.hasNext()) {
1829
			XMLEvent next = readNoWhitespace(reader);
1830
			if (isMyEndingElement(next, parentEvent)) {
1831
				state.putFeatureToCharSorterList(feature);
1832
				TextData textData = TextData.NewInstance(feature);
1833
				textData.putText(getDefaultLanguage(state), text);
1834
				result.add(textData);
1835
				return result;
1836
			} else if (isStartingElement(next, FIGURE_REF)) {
1837
				//TODO
1838
				handleNotYetImplementedElement(next);
1839
			} else if (isStartingElement(next, FOOTNOTE_REF)) {
1840
				//TODO
1841
				handleNotYetImplementedElement(next);
1842
			} else if (isStartingElement(next, BR)) {
1843
				text += "<br/>";
1844
				isTextMode = false;
1845
			} else if (isEndingElement(next, BR)) {
1846
				isTextMode = true;
1847
			} else if (isHtml(next)) {
1848
				text += getXmlTag(next);
1849
			} else if (next.isStartElement()) {
1850
				if (isStartingElement(next, ANNOTATION)) {
1851
					handleNotYetImplementedElement(next); //TODO test handleSimpleAnnotation
1852
				} else if (isStartingElement(next, ITALICS)) {
1853
					handleNotYetImplementedElement(next);
1854
				} else if (isStartingElement(next, BOLD)) {
1855
					handleNotYetImplementedElement(next);
1856
				} else if (isStartingElement(next, FIGURE)) {
1857
					handleFigure(state, reader, next);
1858
				} else if (isStartingElement(next, SUB_CHAR)) {
1859
					List<TextData> textData = handleChar(state, reader, next, feature);
1860
					result.addAll(textData);
1861
				} else if (isStartingElement(next, FOOTNOTE)) {
1862
					FootnoteDataHolder footnote = handleFootnote(state, reader,	next);
1863
					if (footnote.isRef()) {
1864
						String message = "Ref footnote not implemented here";
1865
						fireWarningEvent(message, next, 4);
1866
					} else {
1867
						registerGivenFootnote(state, footnote);
1868
					}
1869
				} else {
1870
					handleUnexpectedStartElement(next.asStartElement());
1871
				}
1872
			} else if (next.isCharacters()) {
1873
				if (!isTextMode) {
1874
					String message = "String is not in text mode";
1875
					fireWarningEvent(message, next, 6);
1876
				} else {
1877
					text += next.asCharacters().getData();
1878
				}
1879
			} else {
1880
				handleUnexpectedEndElement(next.asEndElement());
1881
			}
1882
		}
1883
		throw new IllegalStateException("RefPart has no closing tag");
1884
	}
1885

  
1886
	/**
1887
	 * @param classValue
1888
	 * @param state
1889
	 * @param parentEvent
1890
	 * @param parentFeature 
1891
	 * @return
1892
	 * @throws UndefinedTransformerMethodException
1893
	 */
1894
	private Feature makeFeature(String classValue, MarkupImportState state, XMLEvent parentEvent, Feature parentFeature) {
1895
		UUID uuid;
1896
		try {
1897
			String featureText = StringUtils.capitalize(classValue);
1898
			if (parentFeature != null){
1899
				featureText = "<%s>" + featureText;
1900
				featureText = String.format(featureText, parentFeature.getTitleCache());
1901
				classValue = "<%s>" + classValue;
1902
				classValue = String.format(classValue, parentFeature.getTitleCache());
1903
			}
1904

  
1905
			
1906
			Feature feature = state.getTransformer().getFeatureByKey(classValue);
1907
			if (feature != null) {
1908
				return feature;
1909
			}
1910
			uuid = state.getTransformer().getFeatureUuid(classValue);
1911
			
1912
			if (uuid == null){
1913
				uuid = state.getUnknownFeatureUuid(classValue);
1914
			}
1915
			
1916
			if (uuid == null) {
1917
				// TODO
1918
				String message = "Uuid is not defined for '%s'";
1919
				message = String.format(message, classValue);
1920
				fireWarningEvent(message, parentEvent, 8);
1921
				uuid = UUID.randomUUID();
1922
				state.putUnknownFeatureUuid(classValue, uuid);
1923
			}
1924

  
1925
			// TODO eFlora vocabulary
1926
			TermVocabulary<Feature> voc = null;
1927
			feature = getFeature(state, uuid, featureText, featureText, classValue, voc);
1928
			if (parentFeature != null){
1929
				parentFeature.addIncludes(feature);
1930
				save(parentFeature, state);
1931
			}
1932
			save(feature, state);
1933
					
1934
			if (feature == null) {
1935
				throw new NullPointerException(classValue + " not recognized as a feature");
1936
			}
1937
//			state.putFeatureToCurrentList(feature);
1938
			return feature;
1939
		} catch (Exception e) {
1940
			String message = "Could not create feature for %s: %s";
1941
			message = String.format(message, classValue, e.getMessage());
1942
			fireWarningEvent(message, parentEvent, 4);
1943
			state.putUnknownFeatureUuid(classValue, null);
1944
//			e.printStackTrace();
1945
			return Feature.UNKNOWN();
1946
		}
1947
	}
1948 631

  
1949 632
}

Also available in: Unified diff