Project

General

Profile

Download (36.2 KB) Statistics
| Branch: | Tag: | Revision:
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.util.ArrayList;
13
import java.util.HashSet;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.Set;
17
import java.util.UUID;
18

    
19
import javax.xml.stream.XMLEventReader;
20
import javax.xml.stream.XMLStreamException;
21
import javax.xml.stream.events.Attribute;
22
import javax.xml.stream.events.XMLEvent;
23

    
24
import org.apache.commons.lang.StringUtils;
25
import org.apache.log4j.Logger;
26

    
27
import eu.etaxonomy.cdm.common.CdmUtils;
28
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
29
import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
30
import eu.etaxonomy.cdm.model.common.Annotation;
31
import eu.etaxonomy.cdm.model.common.AnnotationType;
32
import eu.etaxonomy.cdm.model.common.Language;
33
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
34
import eu.etaxonomy.cdm.model.common.TermVocabulary;
35
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
36
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
37
import eu.etaxonomy.cdm.model.description.Feature;
38
import eu.etaxonomy.cdm.model.description.TaxonDescription;
39
import eu.etaxonomy.cdm.model.description.TextData;
40
import eu.etaxonomy.cdm.model.location.Country;
41
import eu.etaxonomy.cdm.model.location.NamedArea;
42
import eu.etaxonomy.cdm.model.media.Media;
43
import eu.etaxonomy.cdm.model.reference.Reference;
44
import eu.etaxonomy.cdm.model.taxon.Taxon;
45

    
46
/**
47
 * @author a.mueller
48
 * @created 30.05.2012
49
 *
50
 */
51
public class MarkupFeatureImport extends MarkupImportBase {
52
	@SuppressWarnings("unused")
53
	private static final Logger logger = Logger.getLogger(MarkupFeatureImport.class);
54

    
55
	protected static final String MODS_TITLEINFO = "titleInfo";
56

    
57
	private final MarkupSpecimenImport specimenImport;
58
	private final MarkupNomenclatureImport nomenclatureImport;
59

    
60
	public MarkupFeatureImport(MarkupDocumentImport docImport, MarkupSpecimenImport specimenImport,
61
			 MarkupNomenclatureImport nomenclatureImport) {
62
		super(docImport);
63
		this.specimenImport = specimenImport;
64
		this.nomenclatureImport = nomenclatureImport;
65
		this.featureImport = this;
66
	}
67

    
68
	public void handleFeature(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
69
		Map<String, Attribute> attrs = getAttributes(parentEvent);
70
		Boolean isFreetext = getAndRemoveBooleanAttributeValue(parentEvent, attrs, IS_FREETEXT, false);
71
		String classValue =getAndRemoveRequiredAttributeValue(parentEvent, attrs, CLASS);
72
		checkNoAttributes(attrs, parentEvent);
73

    
74

    
75
		Feature feature = makeFeature(classValue, state, parentEvent, null);
76
		Taxon taxon = state.getCurrentTaxon();
77
		TaxonDescription taxonDescription = getTaxonDescription(taxon, state.getConfig().getSourceReference(), NO_IMAGE_GALLERY, CREATE_NEW);
78
		// TextData figureHolderTextData = null; //for use with one TextData for
79
		// all figure only
80

    
81
		boolean isDescription = feature.equals(Feature.DESCRIPTION());
82
		DescriptionElementBase lastDescriptionElement = null;
83

    
84
		CharOrder charOrder= new CharOrder();
85
		while (reader.hasNext()) {
86
			XMLEvent next = readNoWhitespace(reader);
87
			if (isMyEndingElement(next, parentEvent)) {
88
				state.putFeatureToGeneralSorterList(feature);
89
				return;
90
			} else if (isEndingElement(next, DISTRIBUTION_LIST) || isEndingElement(next, HABITAT_LIST)) {
91
				// only handle list elements
92
			} else if (isStartingElement(next, HEADING)) {
93
				makeFeatureHeading(state, reader, classValue, feature, next);
94
			} else if (isStartingElement(next, WRITER)) {
95
				makeFeatureWriter(state, reader, feature, taxon, next);
96
//			} else if (isStartingElement(next, DISTRIBUTION_LOCALITY)) {
97
//				if (!feature.equals(Feature.DISTRIBUTION())) {
98
//					String message = "Distribution locality only allowed for feature of type 'distribution'";
99
//					fireWarningEvent(message, next, 4);
100
//				}
101
//				handleDistributionLocality(state, reader, next);
102
			} else if (isStartingElement(next, DISTRIBUTION_LIST) || isStartingElement(next, HABITAT_LIST)) {
103
				// only handle single list elements
104
			} else if (isStartingElement(next, HABITAT)) {
105
				if (!(feature.equals(Feature.HABITAT())
106
						|| feature.equals(Feature.HABITAT_ECOLOGY())
107
						|| feature.equals(Feature.ECOLOGY()))) {
108
					String message = "Habitat only allowed for feature of type 'habitat','habitat ecology' or 'ecology'";
109
					fireWarningEvent(message, next, 4);
110
				}
111
				handleHabitat(state, reader, next);
112
			} else if (isStartingElement(next, CHAR)) {
113
				List<TextData> textDataList = handleChar(state, reader, next, null, charOrder);
114
				charOrder = charOrder.next();
115
				for (TextData textData : textDataList){
116
					taxonDescription.addElement(textData);
117
				}
118
			} else if (isStartingElement(next, STRING)) {
119
				lastDescriptionElement = makeFeatureString(state, reader,feature, taxonDescription, lastDescriptionElement,next, isFreetext);
120
			} else if (isStartingElement(next, FIGURE_REF)) {
121
				lastDescriptionElement = makeFeatureFigureRef(state, reader, taxonDescription, isDescription, lastDescriptionElement, next);
122
			} else if (isStartingElement(next, REFERENCES)) {
123
				// TODO details/microcitation ??
124

    
125
				List<Reference> refs = handleReferences(state, reader, next);
126
				if (!refs.isEmpty()) {
127
					// TODO
128
					Reference descriptionRef = state.getConfig().getSourceReference();
129
					TaxonDescription description = getTaxonDescription(taxon, descriptionRef, false, true);
130
					TextData featurePlaceholder = docImport.getFeaturePlaceholder(state, description, feature, true);
131
					for (Reference citation : refs) {
132
						featurePlaceholder.addSource(OriginalSourceType.PrimaryTaxonomicSource, null, null, citation, null);
133
					}
134
				} else {
135
					String message = "No reference found in references";
136
					fireWarningEvent(message, next, 6);
137
				}
138
			} else if (isStartingElement(next, NUM)) {
139
				//TODO
140
				handleNotYetImplementedElement(next);
141
			} else {
142
				handleUnexpectedElement(next);
143
			}
144
		}
145
		throw new IllegalStateException("<Feature> has no closing tag");
146
	}
147

    
148

    
149
	/**
150
	 * @param state
151
	 * @param reader
152
	 * @param taxonDescription
153
	 * @param isDescription
154
	 * @param lastDescriptionElement
155
	 * @param next
156
	 * @return
157
	 * @throws XMLStreamException
158
	 */
159
	public DescriptionElementBase makeFeatureFigureRef(MarkupImportState state, XMLEventReader reader,TaxonDescription taxonDescription,
160
					boolean isDescription, DescriptionElementBase lastDescriptionElement, XMLEvent next) throws XMLStreamException {
161
		FigureDataHolder figureHolder = handleFigureRef(state, reader, next);
162
		Feature figureFeature = getFeature(state, MarkupTransformer.uuidFigures, "Figures", "Figures", "Fig.",null);
163
		if (isDescription) {
164
			TextData figureHolderTextData = null;
165
			// if (figureHolderTextData == null){
166
			figureHolderTextData = TextData.NewInstance(figureFeature);
167
			if (StringUtils.isNotBlank(figureHolder.num)) {
168
				String annotationText = "<num>" + figureHolder.num.trim() + "</num>";
169
				Annotation annotation = Annotation.NewInstance(annotationText, AnnotationType.TECHNICAL(), getDefaultLanguage(state));
170
				figureHolderTextData.addAnnotation(annotation);
171
			}
172
			if (StringUtils.isNotBlank(figureHolder.figurePart)) {
173
				String annotationText = "<figurePart>"+ figureHolder.figurePart.trim() + "</figurePart>";
174
				Annotation annotation = Annotation.NewInstance(annotationText,AnnotationType.EDITORIAL(), getDefaultLanguage(state));
175
				figureHolderTextData.addAnnotation(annotation);
176
			}
177
			// if (StringUtils.isNotBlank(figureText)){
178
			// figureHolderTextData.putText(language, figureText);
179
			// }
180
			taxonDescription.addElement(figureHolderTextData);
181
			// }
182
			registerFigureDemand(state, next, figureHolderTextData, figureHolder.ref);
183
		} else {
184
			if (lastDescriptionElement == null) {
185
				String message = "No description element created yet that can be referred by figure. Create new TextData instead";
186
				fireWarningEvent(message, next, 4);
187
				lastDescriptionElement = TextData.NewInstance(figureFeature);
188
				taxonDescription.addElement(lastDescriptionElement);
189
			}
190
			registerFigureDemand(state, next, lastDescriptionElement,	figureHolder.ref);
191
		}
192
		return lastDescriptionElement;
193
	}
194

    
195
	/**
196
	 * @param state
197
	 * @param reader
198
	 * @param feature
199
	 * @param taxonDescription
200
	 * @param lastDescriptionElement
201
	 * @param distributionList
202
	 * @param next
203
	 * @return
204
	 * @throws XMLStreamException
205
	 * @throws
206
	 */
207
	private DescriptionElementBase makeFeatureString(MarkupImportState state,XMLEventReader reader, Feature feature,
208
				TaxonDescription taxonDescription, DescriptionElementBase lastDescriptionElement, XMLEvent next, Boolean isFreetext) throws XMLStreamException {
209

    
210
		//for specimen only
211
		if (feature.equals(Feature.SPECIMEN()) || feature.equals(Feature.MATERIALS_EXAMINED())
212
				|| feature.getUuid().equals(MarkupTransformer.uuidWoodSpecimens)){
213

    
214
			List<DescriptionElementBase> specimens = specimenImport.handleMaterialsExamined(state, reader, next, feature, taxonDescription);
215
			for (DescriptionElementBase specimen : specimens){
216
				if (specimen.getInDescription() == null){
217
					taxonDescription.addElement(specimen);
218
				}
219
				lastDescriptionElement = specimen;
220
			}
221
			state.setCurrentCollector(null);
222

    
223
			return lastDescriptionElement;
224
		}else if (feature.equals(Feature.COMMON_NAME()) && (isFreetext == null || !isFreetext)){
225
			List<DescriptionElementBase> commonNames = makeCommonNameString(state, reader, next);
226
			//NODE: we do also have the old version makeVernacular, which was called from "others" below
227
			for (DescriptionElementBase commonName : commonNames){
228
				taxonDescription.addElement(commonName);
229
				lastDescriptionElement = commonName;
230
			}
231
			return lastDescriptionElement;
232
		}
233
		else{
234

    
235
			//others
236
			Map<String, String> subheadingMap = handleString(state, reader, next, feature);
237
			for (String subheading : subheadingMap.keySet()) {
238
				Feature subheadingFeature = feature;
239
				if (StringUtils.isNotBlank(subheading) && subheadingMap.size() > 1) {
240
					subheadingFeature = makeFeature(subheading, state, next, null);
241
				}
242
				if (feature.equals(Feature.COMMON_NAME()) && (isFreetext == null || !isFreetext)){
243
					//NOTE: see above
244
//					List<DescriptionElementBase> commonNames = makeVernacular(state, subheading, subheadingMap.get(subheading));
245
//					for (DescriptionElementBase commonName : commonNames){
246
//						taxonDescription.addElement(commonName);
247
//						lastDescriptionElement = commonName;
248
//					}
249
				}else {
250
					TextData textData = TextData.NewInstance(subheadingFeature);
251
					textData.putText(getDefaultLanguage(state), subheadingMap.get(subheading));
252
					taxonDescription.addElement(textData);
253
					lastDescriptionElement = textData;
254
					// TODO how to handle figures when these data are split in
255
					// subheadings
256
				}
257
			}
258
			return lastDescriptionElement;
259
		}
260
	}
261

    
262
	/**
263
	 * @param classValue
264
	 * @param state
265
	 * @param parentEvent
266
	 * @param parentFeature
267
	 * @return
268
	 * @throws UndefinedTransformerMethodException
269
	 */
270
	private Feature makeFeature(String classValue, MarkupImportState state, XMLEvent parentEvent, Feature parentFeature) {
271
		UUID uuid;
272
		try {
273
			String featureText = StringUtils.capitalize(classValue);
274
			if (parentFeature != null){
275
				featureText = "<%s>" + featureText;
276
				featureText = String.format(featureText, parentFeature.getTitleCache());
277
				classValue = "<%s>" + classValue;
278
				classValue = String.format(classValue, parentFeature.getTitleCache());
279
			}
280

    
281

    
282
			//get existing feature
283
			if (classValue.endsWith(".")){
284
			    classValue = classValue.substring(0, classValue.length() - 1);
285
			}
286
			Feature feature = state.getTransformer().getFeatureByKey(classValue);
287
			if (feature != null) {
288
				return feature;
289
			}
290
			uuid = state.getTransformer().getFeatureUuid(classValue);
291

    
292
			if (uuid == null){
293
				uuid = state.getUnknownFeatureUuid(classValue);
294
			}
295

    
296
			if (uuid == null) {
297
				// TODO
298
				String message = "Uuid is not defined for '%s'";
299
				message = String.format(message, classValue);
300
				if (! message.contains("<")){
301
					//log only top level features
302
					fireWarningEvent(message, parentEvent, 8);
303
				}
304
				uuid = UUID.randomUUID();
305
				state.putUnknownFeatureUuid(classValue, uuid);
306
			}
307

    
308
			// TODO eFlora vocabulary
309
			TermVocabulary<Feature> voc = null;
310
			feature = getFeature(state, uuid, featureText, featureText, classValue, voc);
311
			if (parentFeature != null){
312
				parentFeature.addIncludes(feature);
313
				save(parentFeature, state);
314
			}
315
			save(feature, state);
316

    
317
			if (feature == null) {
318
				throw new NullPointerException(classValue + " not recognized as a feature");
319
			}
320
//			state.putFeatureToCurrentList(feature);
321
			return feature;
322
		} catch (Exception e) {
323
			String message = "Could not create feature for %s: %s";
324
			message = String.format(message, classValue, e.getMessage());
325
			fireWarningEvent(message, parentEvent, 4);
326
			state.putUnknownFeatureUuid(classValue, null);
327
//			e.printStackTrace();
328
			return Feature.UNKNOWN();
329
		}
330
	}
331

    
332
	public class CharOrder{
333
		static final int strlength = 3;
334
		private int order = 1;
335
		private CharOrder parent;
336
		private final List<CharOrder> children = new ArrayList<CharOrder>();
337

    
338
		public CharOrder nextChild(){
339
			CharOrder result = new CharOrder();
340
			if (! children.isEmpty()) {
341
				result.order = children.get(children.size() - 1).order + 1;
342
			}
343
			result.parent = this;
344
			children.add(result);
345
			return result;
346
		}
347

    
348
		public CharOrder next(){
349
			CharOrder result = new CharOrder();
350
			result.order = order + 1;
351
			result.parent = parent;
352
			if (parent != null){
353
				parent.children.add(result);
354
			}
355
			return result;
356
		}
357

    
358
		public String orderString(){
359
			String parentString = parent == null ? "" : parent.orderString();
360
			String result = CdmUtils.concat("-", parentString, StringUtils.leftPad(String.valueOf(order), strlength, '0'));
361
			return result;
362
		}
363

    
364
		@Override
365
        public String toString(){
366
			return orderString();
367
		}
368
	}
369

    
370

    
371
	/**
372
	 * Handle the char or subchar element. As
373
	 * @param state the import state
374
	 * @param reader
375
	 * @param parentEvent
376
	 * @param parentFeature in case of subchars we need to attache the newly created feature to a parent feature, should be <code>null</code>
377
	 * for top level chars.
378
	 * @return List of TextData. Not a single one as the recursive TextData will also be returned
379
	 * @throws XMLStreamException
380
	 */
381
	private List<TextData> handleChar(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, Feature parentFeature, CharOrder myCharOrder) throws XMLStreamException {
382
		List<TextData> result = new ArrayList<TextData>();
383
		String classValue = getClassOnlyAttribute(parentEvent);
384
		Feature feature = makeFeature(classValue, state, parentEvent, parentFeature);
385
		if(parentFeature == null){
386
			state.putFeatureToCharSorterList(feature);
387
		}else{
388
			FeatureSorterInfo parentInfo = state.getLatestCharFeatureSorterInfo();
389
//			if (! parentInfo.getUuid().equals(parentFeature.getUuid())){
390
//				String message = "The parent char feature is not the same as the latest feature. This is the case for char hierarchies with > 2 levels, which is not yet handled by the import";
391
//				fireWarningEvent(message, parentEvent, 6);
392
//			}else{
393
				state.getLatestCharFeatureSorterInfo().addSubFeature(new FeatureSorterInfo(feature));
394
//			}
395
		}
396

    
397
		TextData textData = TextData.NewInstance(feature);
398
		result.add(textData);
399

    
400
		AnnotationType annType = getAnnotationType(state, MarkupTransformer.uuidOriginalOrder, "Original order", "Order in original treatment", null, AnnotationType.TECHNICAL().getVocabulary());
401
		textData.addAnnotation(Annotation.NewInstance(myCharOrder.orderString(), annType, Language.ENGLISH()));
402

    
403
		boolean isTextMode = true;
404
		String text = "";
405
		while (reader.hasNext()) {
406
			XMLEvent next = readNoWhitespace(reader);
407
			if (isMyEndingElement(next, parentEvent)) {
408
				text = text.trim();
409
				textData.putText(getDefaultLanguage(state), text);
410
				return result;
411
			} else if (isStartingElement(next, FIGURE_REF)) {
412
				//TODO
413
				handleNotYetImplementedElement(next);
414
			} else if (isStartingElement(next, FOOTNOTE_REF)) {
415
				//TODO
416
				handleNotYetImplementedElement(next);
417
			} else if (isStartingElement(next, BR)) {
418
				text += "<br/>";
419
				isTextMode = false;
420
			} else if (isEndingElement(next, BR)) {
421
				isTextMode = true;
422
			} else if (isHtml(next)) {
423
				text += getXmlTag(next);
424
			} else if (next.isStartElement()) {
425
				if (isStartingElement(next, ANNOTATION)) {
426
					handleNotYetImplementedElement(next); //TODO test handleSimpleAnnotation
427
				} else if (isStartingElement(next, ITALICS)) {
428
					handleNotYetImplementedElement(next);
429
				} else if (isStartingElement(next, BOLD)) {
430
					handleNotYetImplementedElement(next);
431
				} else if (isStartingElement(next, FIGURE)) {
432
					handleFigure(state, reader, next, specimenImport, nomenclatureImport);
433
				} else if (isStartingElement(next, SUB_CHAR)) {
434
					List<TextData> subTextData = handleChar(state, reader, next, feature, myCharOrder.nextChild());
435
					result.addAll(subTextData);
436
				} else if (isStartingElement(next, FOOTNOTE)) {
437
					FootnoteDataHolder footnote = handleFootnote(state, reader,	next, specimenImport, nomenclatureImport);
438
					if (footnote.isRef()) {
439
						String message = "Ref footnote not implemented here";
440
						fireWarningEvent(message, next, 4);
441
					} else {
442
						registerGivenFootnote(state, footnote);
443
					}
444
				} else {
445
					handleUnexpectedStartElement(next.asStartElement());
446
				}
447
			} else if (next.isCharacters()) {
448
				if (!isTextMode) {
449
					String message = "String is not in text mode";
450
					fireWarningEvent(message, next, 6);
451
				} else {
452
					text += next.asCharacters().getData();
453
				}
454
			} else {
455
				handleUnexpectedEndElement(next.asEndElement());
456
			}
457
		}
458
		throw new IllegalStateException("RefPart has no closing tag");
459
	}
460

    
461

    
462
	/**
463
	 * @param state
464
	 * @param reader
465
	 * @param classValue
466
	 * @param feature
467
	 * @param next
468
	 * @throws XMLStreamException
469
	 */
470
	private void makeFeatureHeading(MarkupImportState state, XMLEventReader reader, String classValue, Feature feature, XMLEvent next) throws XMLStreamException {
471
		String heading = handleHeading(state, reader, next);
472
		if (StringUtils.isNotBlank(heading)) {
473
			if (!heading.equalsIgnoreCase(classValue)) {
474
				try {
475
					if (!feature.equals(state.getTransformer().getFeatureByKey(heading))) {
476
						UUID headerFeatureUuid = state.getTransformer().getFeatureUuid(heading);
477
						if (!feature.getUuid().equals(headerFeatureUuid)) {
478
							String message = "Feature heading '%s' differs from feature class '%s' and can not be transformed to feature";
479
							message = String.format(message, heading, classValue);
480
							fireWarningEvent(message, next, 1);
481
						}
482
					}
483
				} catch (UndefinedTransformerMethodException e) {
484
					throw new RuntimeException(e);
485
				}
486
			} else {
487
				// do nothing
488
			}
489
		}
490
	}
491

    
492

    
493
	/**
494
	 * @param state
495
	 * @param reader
496
	 * @param feature
497
	 * @param taxon
498
	 * @param next
499
	 * @throws XMLStreamException
500
	 */
501
	private void makeFeatureWriter(MarkupImportState state,XMLEventReader reader, Feature feature, Taxon taxon, XMLEvent next) throws XMLStreamException {
502
		WriterDataHolder writer = handleWriter(state, reader, next);
503
		if (isNotBlank(writer.writer)) {
504
			// TODO
505
			Reference ref = state.getConfig().getSourceReference();
506
			TaxonDescription description = getTaxonDescription(taxon, ref,
507
					false, true);
508
			TextData featurePlaceholder = docImport.getFeaturePlaceholder(state,
509
					description, feature, true);
510
			featurePlaceholder.addAnnotation(writer.annotation);
511
			registerFootnotes(state, featurePlaceholder, writer.footnotes);
512
		} else {
513
			String message = "Writer element is empty";
514
			fireWarningEvent(message, next, 4);
515
		}
516
	}
517

    
518

    
519
	protected void handleHabitat(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
520
		checkNoAttributes(parentEvent);
521
		Taxon taxon = state.getCurrentTaxon();
522
		// TODO which ref to take?
523
		Reference ref = state.getConfig().getSourceReference();
524

    
525

    
526
		boolean isTextMode = true;
527
		String text = "";
528
		while (reader.hasNext()) {
529
			XMLEvent next = readNoWhitespace(reader);
530
			if (isMyEndingElement(next, parentEvent)) {
531
				TaxonDescription description = getTaxonDescription(taxon, ref,
532
						false, true);
533
				UUID uuidExtractedHabitat = MarkupTransformer.uuidExtractedHabitat;
534
				Feature feature = getFeature(
535
						state,
536
						uuidExtractedHabitat,
537
						"Extracted Habitat",
538
						"An structured habitat that was extracted from a habitat text",
539
						"extr. habit.", null);
540
				TextData habitat = TextData.NewInstance(feature);
541
				habitat.putText(getDefaultLanguage(state), text);
542
				description.addElement(habitat);
543

    
544
				return;
545
			} else if (isStartingElement(next, ALTITUDE)) {
546
				text = text.trim() + getTaggedCData(state, reader, next);
547
			} else if (isStartingElement(next, LIFE_CYCLE_PERIODS)) {
548
				handleNotYetImplementedElement(next);
549
			} else if (next.isCharacters()) {
550
			    if (! isTextMode) {
551
                    String message = "String is not in text mode";
552
                    fireWarningEvent(message, next, 6);
553
                } else {
554
                    text += next.asCharacters().getData();
555
                }
556
	         } else if (isStartingElement(next, BR)) {
557
	                text += "<br/>";
558
	                isTextMode = false;
559
	        } else if (isEndingElement(next, BR)) {
560
	                isTextMode = true;
561
	        } else if (isStartingElement(next, REFERENCES)) {
562
	            handleNotYetImplementedElement(next);
563
	        } else if (isStartingElement(next, FIGURE_REF)) {
564
                handleNotYetImplementedElement(next);
565
            } else {
566
                String type = next.toString();
567
                String location = String.valueOf(next.getLocation().getLineNumber());
568
                System.out.println("MarkupFeature.handleHabitat: Unexpected element in habitat: " + type + ":  " + location);
569
				handleUnexpectedElement(next);
570
			}
571
		}
572
		throw new IllegalStateException("<Habitat> has no closing tag");
573
	}
574

    
575

    
576
	private FigureDataHolder handleFigureRef(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent)
577
			throws XMLStreamException {
578
		FigureDataHolder result = new FigureDataHolder();
579
		Map<String, Attribute> attributes = getAttributes(parentEvent);
580
		result.ref = getAndRemoveAttributeValue(attributes, REF);
581
		checkNoAttributes(attributes, parentEvent);
582

    
583
		// text is not handled, needed only for debugging purposes
584
		String text = "";
585
		while (reader.hasNext()) {
586
			XMLEvent next = readNoWhitespace(reader);
587
			if (isMyEndingElement(next, parentEvent)) {
588
				return result;
589
			} else if (isStartingElement(next, NUM)) {
590
				String num = getCData(state, reader, next);
591
				result.num = num; // num is not handled during import
592
			} else if (isStartingElement(next, FIGURE_PART)) {
593
				result.figurePart = getCData(state, reader, next);
594
			} else if (next.isCharacters()) {
595
				text += next.asCharacters().getData();
596
			} else {
597
				fireUnexpectedEvent(next, 0);
598
			}
599
		}
600
		throw new IllegalStateException("<figureRef> has no end tag");
601
	}
602

    
603

    
604
	private void registerFigureDemand(MarkupImportState state, XMLEvent next, AnnotatableEntity entity, String figureRef) {
605
		Media existingFigure = state.getFigure(figureRef);
606
		if (existingFigure != null) {
607
			attachFigure(state, next, entity, existingFigure);
608
		} else {
609
			Set<AnnotatableEntity> demands = state.getFigureDemands(figureRef);
610
			if (demands == null) {
611
				demands = new HashSet<AnnotatableEntity>();
612
				state.putFigureDemands(figureRef, demands);
613
			}
614
			demands.add(entity);
615
		}
616
	}
617

    
618
	private List<DescriptionElementBase> makeCommonNameString(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException{
619

    
620
		List<DescriptionElementBase> result = new ArrayList<DescriptionElementBase>();
621

    
622
		checkNoAttributes(parentEvent);
623

    
624
		while (reader.hasNext()) {
625
			XMLEvent next = readNoWhitespace(reader);
626
			if (isMyEndingElement(next, parentEvent)) {
627
				if (result.isEmpty()){
628
					fireWarningEvent("Common name was not created", next, 4);
629
				}
630
				return result;
631
			} else if (isStartingElement(next, VERNACULAR_NAMES)) {
632
				result = makeVernacularNames(state, reader, next);
633
			} else if (isStartingElement(next, SUB_HEADING)) {
634
				String subheading = getCData(state, reader, next);
635
				if (! subheading.matches("(Nom(s)? vernaculaire(s)?\\:|Vern.)")){
636
					fireWarningEvent("Subheading for vernacular name not recognized: " + subheading, next, 4);
637
				}
638
			} else if (next.isCharacters()) {
639
				String chars = next.asCharacters().toString().trim();
640
				if (chars.equals(".")){
641
					//do nothing
642
				}else{
643
					fireWarningEvent("Character not handled in vernacular name: " + chars, next, 4);
644
				}
645
			} else if (isStartingElement(next, REFERENCES)) {
646
			    handleNotYetImplementedElement(next);
647
            }else {
648
				handleUnexpectedElement(next);
649
			}
650
		}
651
		throw new IllegalStateException("closing tag is missing");
652

    
653

    
654
	}
655

    
656
	private List<DescriptionElementBase> makeVernacularNames(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException{
657
		List<DescriptionElementBase> result = new ArrayList<DescriptionElementBase>();
658
		checkNoAttributes(parentEvent);
659

    
660
		while (reader.hasNext()) {
661
			XMLEvent next = readNoWhitespace(reader);
662
			if (isMyEndingElement(next, parentEvent)) {
663
				state.removeCurrentAreas();
664
				return result;
665
			} else if (isStartingElement(next, VERNACULAR_NAME)) {
666
				List<CommonTaxonName> names = makeSingleVernacularName(state, reader, next);
667
				result.addAll(names);
668
			} else if (isStartingElement(next, SUB_HEADING)) {
669
				makeVernacularNamesSubHeading(state, reader, next);
670
			} else {
671
				handleUnexpectedElement(next);
672
			}
673
		}
674
		throw new IllegalStateException("closing tag is missing");
675

    
676
	}
677

    
678
	private void makeVernacularNamesSubHeading(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
679
		checkNoAttributes(parentEvent);
680

    
681
		String text = "";
682
		while (reader.hasNext()) {
683
			XMLEvent next = readNoWhitespace(reader);
684
			if (isMyEndingElement(next, parentEvent)) {
685
				if (StringUtils.isNotBlank(text)){
686
					NamedArea area = getCommonNameArea(text);
687
					if (area != null){
688
						state.removeCurrentAreas();
689
						state.addCurrentArea(area);
690
					}else{
691
						fireWarningEvent("Vernacular subheading not recognized", next, 8);
692
					}
693
				}
694

    
695
				return ;
696
			} else if (next.isCharacters()) {
697
				text += next.asCharacters().getData();
698
			} else {
699
				handleUnexpectedElement(next);
700
			}
701
		}
702
		throw new IllegalStateException("closing tag is missing");
703

    
704
	}
705

    
706
	private NamedArea getCommonNameArea(String text) {
707
		if (text.endsWith(":")){
708
			text = text.substring(0, text.length()-1);
709
		}
710

    
711
		// for now we do it hardcoded
712
		if (text.equalsIgnoreCase("Guyana")){
713
			return Country.GUYANAREPUBLICOF();
714
		}else if (text.equalsIgnoreCase("Suriname")){
715
			return Country.SURINAMEREPUBLICOF();
716
		}else if (text.equalsIgnoreCase("French Guiana")){
717
			return Country.FRENCHGUIANA();
718
		}
719
		return null;
720
	}
721

    
722
	private List<CommonTaxonName> makeSingleVernacularName(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException{
723
		checkNoAttributes(parentEvent);
724
		List<CommonTaxonName> result = new ArrayList<CommonTaxonName>();
725

    
726
		Language language = state.getDefaultLanguage();
727
		while (reader.hasNext()) {
728
			XMLEvent next = readNoWhitespace(reader);
729
			if (isMyEndingElement(next, parentEvent)) {
730
				for (CommonTaxonName commonName : result){
731
					commonName.setLanguage(language);
732
				}
733
//				if (isNotBlank(name)){
734
//					result.setName(name);
735
//				}else{
736
//					fireWarningEvent("No name string for common name", parentEvent, 4);
737
//				}
738

    
739
				return result;
740
			} else if (isStartingElement(next, NAME)) {
741
				//TODO test
742
				CommonTaxonName name = handleVernacularNameName(state, reader, next);
743
				if (name != null){
744
					result.add(name);
745
				}
746
			} else if (isStartingElement(next, LOCAL_LANGUAGE)) {
747
				Language localLanguage = handleLocalLanguage(state, reader, next);
748
				if (localLanguage != null){
749
					language = localLanguage;
750
				}
751
			} else if (isStartingElement(next, TRANSLATION)) {
752
				//TODO
753
				handleNotYetImplementedElement(next);
754
			} else if (isStartingElement(next, LOCALITY)) {
755
				//TODO
756
				handleNotYetImplementedElement(next);
757
			} else if (isStartingElement(next, ANNOTATION)){
758
				//TODO
759
				handleNotYetImplementedElement(next);
760
			} else if (isStartingElement(next, FOOTNOTE_REF)) {
761
				//TODO
762
				handleNotYetImplementedElement(next);
763
			} else if (next.isCharacters()) {
764
				String chars = next.asCharacters().toString().trim();
765
				if (chars.equals("(") || chars.equals(")") || chars.equals(",")){
766
					//do nothing
767
				}else{
768
					fireWarningEvent("Character not handled in vernacular name: " + chars, next, 4);
769
				}
770
			} else {
771
				handleUnexpectedElement(next);
772
			}
773
		}
774
		throw new IllegalStateException("closing tag is missing");
775
	}
776

    
777
	private CommonTaxonName handleVernacularNameName(MarkupImportState state, XMLEventReader reader,
778
				XMLEvent parentEvent) throws XMLStreamException {
779
		//attributes
780
		Map<String, Attribute> attributes = getAttributes(parentEvent);
781
		this.checkAndRemoveAttributeValue(attributes, CLASS, "vernacular");
782
		this.checkNoAttributes(attributes, parentEvent);
783

    
784
		//
785
		String text = getCData(state, reader, parentEvent, false);
786
		CommonTaxonName name = CommonTaxonName.NewInstance(text, null);
787
		if (! state.getCurrentAreas().isEmpty()){
788
			if (state.getCurrentAreas().size() > 1){
789
				fireWarningEvent("Multiple areas for common name not yet covered by CDM", parentEvent , 8);
790
			}else{
791
				name.setArea(state.getCurrentAreas().iterator().next());
792
			}
793
		}
794
		return name;
795
	}
796

    
797
	private Language handleLocalLanguage(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
798
		//attributes
799
		Map<String, Attribute> attributes = getAttributes(parentEvent);
800
		boolean doubtful = getAndRemoveBooleanAttributeValue(parentEvent, attributes, DOUBTFUL, false);
801
		boolean unknown = getAndRemoveBooleanAttributeValue(parentEvent, attributes, UNKNOWN, false);
802
		this.checkNoAttributes(attributes, parentEvent);
803

    
804
		if (doubtful == true){
805
			fireWarningEvent("Doubtful not yet implemented for local language", parentEvent, 2);
806
		}
807
		if (unknown == true){
808
			fireWarningEvent("Unknown not yet implemented for local language ", parentEvent, 2);
809
		}
810

    
811
		//
812
		String text = getCData(state, reader, parentEvent);
813
		Language lang = makeLanguageByLangStr(state, text);
814
		return lang;
815

    
816
	}
817

    
818
	private List<DescriptionElementBase> makeVernacular(MarkupImportState state, String subheading, String commonNameString) throws XMLStreamException {
819
		List<DescriptionElementBase> result = new ArrayList<DescriptionElementBase>();
820
		String[] splits = commonNameString.split(",");
821
		for (String split : splits){
822
			split = split.trim();
823
			if (! split.matches(".*\\(.*\\)\\.?")){
824
				fireWarningEvent("Common name string '"+split+"' does not match given pattern", state.getReader().peek(), 4);
825
			}
826

    
827
			String name = split.replaceAll("\\(.*\\)", "").replace(".", "").trim();
828
			String languageStr = split.replaceFirst(".*\\(", "").replaceAll("\\)\\.?", "").trim();
829

    
830
			Language language = null;
831
			if (StringUtils.isNotBlank(languageStr)){
832
				language = makeLanguageByLangStr(state, languageStr);
833
			}
834
			DescriptionElementBase commonName;
835
			if (name != null && name.length() < 255 ){
836
				NamedArea area = null;
837
				commonName = CommonTaxonName.NewInstance(name, language, area);
838
			}else{
839
				if (language == null){
840
					language = getDefaultLanguage(state);
841
				}
842
				commonName = TextData.NewInstance(Feature.COMMON_NAME(), name, language, null);
843
				String warning = "Vernacular feature is >255 size. Therefore it is handled as TextData, not CommonTaxonName: " + name;
844
				fireWarningEvent(warning, state.getReader().peek(), 1);
845
			}
846
			result.add(commonName);
847
		}
848

    
849
		return result;
850
	}
851

    
852
	private Language makeLanguageByLangStr(MarkupImportState state, String languageStr) throws XMLStreamException {
853
		try {
854
			Language language = state.getTransformer().getLanguageByKey(languageStr);
855
			if (language == null){
856
				UUID langUuid = state.getTransformer().getLanguageUuid(languageStr);
857
				TermVocabulary<?> voc = null;
858
				language = getLanguage(state, langUuid, languageStr, languageStr, null, voc);
859
			}
860
			if (language == null){
861
				String warning = "Language " + languageStr + " not recognized by transformer";
862
				fireWarningEvent(warning, state.getReader().peek(), 4);
863
			}
864
			return language;
865
		} catch (UndefinedTransformerMethodException e) {
866
			throw new RuntimeException(e);
867
		}
868
	}
869

    
870

    
871
	private String handleHeading(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent)throws XMLStreamException {
872
		checkNoAttributes(parentEvent);
873

    
874
		String text = "";
875
		while (reader.hasNext()) {
876
			XMLEvent next = readNoWhitespace(reader);
877
			if (isMyEndingElement(next, parentEvent)) {
878
				return text;
879
			} else if (next.isStartElement()) {
880
				if (isStartingElement(next, FOOTNOTE)) {
881
					handleNotYetImplementedElement(next);
882
				} else {
883
					handleUnexpectedStartElement(next.asStartElement());
884
				}
885
			} else if (next.isCharacters()) {
886
				text += next.asCharacters().getData();
887
			} else {
888
				handleUnexpectedEndElement(next.asEndElement());
889
			}
890
		}
891
		throw new IllegalStateException("<String> has no closing tag");
892

    
893
	}
894

    
895

    
896
	private List<Reference> handleReferences(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
897
		// attributes
898
		Map<String, Attribute> attributes = getAttributes(parentEvent);
899
		String bibliography = getAndRemoveAttributeValue(attributes,
900
				BIBLIOGRAPHY);
901
		String serialsAbbreviations = getAndRemoveAttributeValue(attributes,
902
				SERIALS_ABBREVIATIONS);
903
		if (isNotBlank(bibliography) || isNotBlank(serialsAbbreviations)) {
904
			String message = "Attributes not yet implemented for <references>";
905
			fireWarningEvent(message, parentEvent, 4);
906
		}
907

    
908
		List<Reference> result = new ArrayList<Reference>();
909

    
910
		// elements
911
		while (reader.hasNext()) {
912
			XMLEvent next = readNoWhitespace(reader);
913
			if (next.isEndElement()) {
914
				if (isMyEndingElement(next, parentEvent)) {
915
					return result;
916
				} else {
917
					if (isEndingElement(next, HEADING)) {
918
						// NOT YET IMPLEMENTED
919
						popUnimplemented(next.asEndElement());
920
					} else if (isEndingElement(next, WRITER)) {
921
						// NOT YET IMPLEMENTED
922
						popUnimplemented(next.asEndElement());
923
					} else if (isEndingElement(next, FOOTNOTE)) {
924
						// NOT YET IMPLEMENTED
925
						popUnimplemented(next.asEndElement());
926
					} else if (isEndingElement(next, STRING)) {
927
						// NOT YET IMPLEMENTED
928
						popUnimplemented(next.asEndElement());
929
					} else if (isEndingElement(next, REF_NUM)) {
930
						// NOT YET IMPLEMENTED
931
						popUnimplemented(next.asEndElement());
932
					} else {
933
						handleUnexpectedEndElement(next.asEndElement());
934
					}
935
				}
936
			} else if (next.isStartElement()) {
937
				if (isStartingElement(next, HEADING)) {
938
					handleNotYetImplementedElement(next);
939
				} else if (isStartingElement(next, SUB_HEADING)) {
940
					String subheading = getCData(state, reader, next).trim();
941
					String excludePattern = "(i?)(References?|Literature):?";
942
					if (!subheading.matches(excludePattern)) {
943
						fireNotYetImplementedElement(next.getLocation(), next.asStartElement().getName(), 0);
944
					}
945
				} else if (isStartingElement(next, WRITER)) {
946
					handleNotYetImplementedElement(next);
947
				} else if (isStartingElement(next, FOOTNOTE)) {
948
					handleNotYetImplementedElement(next);
949
				} else if (isStartingElement(next, STRING)) {
950
					handleNotYetImplementedElement(next);
951
				} else if (isStartingElement(next, REF_NUM)) {
952
					handleNotYetImplementedElement(next);
953
				} else if (isStartingElement(next, REFERENCE)) {
954
					Reference ref = nomenclatureImport.handleReference(state, reader, next);
955
					result.add(ref);
956
				} else {
957
					handleUnexpectedStartElement(next);
958
				}
959
			} else {
960
				handleUnexpectedElement(next);
961
			}
962
		}
963
		throw new IllegalStateException("<References> has no closing tag");
964
	}
965

    
966

    
967
	private String getTaggedCData(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
968
		checkNoAttributes(parentEvent);
969

    
970
		String text = getXmlTag(parentEvent);
971
		while (reader.hasNext()) {
972
			XMLEvent next = readNoWhitespace(reader);
973
			if (isMyEndingElement(next, parentEvent)) {
974
				text += getXmlTag(next);
975
				return text;
976
			} else if (next.isStartElement()) {
977
				text += getTaggedCData(state, reader, next);
978
			} else if (next.isEndElement()) {
979
				text += getTaggedCData(state, reader, next);
980
			} else if (next.isCharacters()) {
981
				text += next.asCharacters().getData();
982
			} else {
983
				handleUnexpectedEndElement(next.asEndElement());
984
			}
985
		}
986
		throw new IllegalStateException("Some tag has no closing tag");
987
	}
988
}
(8-8/19)