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 MarkupSpecimenImport specimenImport;
58
	private 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
	}
66

    
67
	public void handleFeature(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
68
		Map<String, Attribute> attrs = getAttributes(parentEvent);
69
		Boolean isFreetext = getAndRemoveBooleanAttributeValue(parentEvent, attrs, IS_FREETEXT, false);
70
		String classValue =getAndRemoveRequiredAttributeValue(parentEvent, attrs, CLASS);
71
		checkNoAttributes(attrs, parentEvent);
72
		
73
		
74
		Feature feature = makeFeature(classValue, state, parentEvent, null);
75
		Taxon taxon = state.getCurrentTaxon();
76
		TaxonDescription taxonDescription = getTaxonDescription(taxon, state.getConfig().getSourceReference(), NO_IMAGE_GALLERY, CREATE_NEW);
77
		// TextData figureHolderTextData = null; //for use with one TextData for
78
		// all figure only
79

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

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

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

    
194
	/**
195
	 * @param state
196
	 * @param reader
197
	 * @param feature
198
	 * @param taxonDescription
199
	 * @param lastDescriptionElement
200
	 * @param distributionList 
201
	 * @param next
202
	 * @return
203
	 * @throws XMLStreamException
204
	 * @throws  
205
	 */
206
	private DescriptionElementBase makeFeatureString(MarkupImportState state,XMLEventReader reader, Feature feature, 
207
				TaxonDescription taxonDescription, DescriptionElementBase lastDescriptionElement, XMLEvent next, Boolean isFreetext) throws XMLStreamException {
208
		
209
		//for specimen only
210
		if (feature.equals(Feature.SPECIMEN()) || feature.equals(Feature.MATERIALS_EXAMINED()) 
211
				|| feature.getUuid().equals(MarkupTransformer.uuidWoodSpecimens)){
212
			
213
			List<DescriptionElementBase> specimens = specimenImport.handleMaterialsExamined(state, reader, next, feature, taxonDescription);
214
			for (DescriptionElementBase specimen : specimens){
215
				if (specimen.getInDescription() == null){
216
					taxonDescription.addElement(specimen);
217
				}
218
				lastDescriptionElement = specimen;
219
			}
220
			state.setCurrentCollector(null);
221
			
222
			return lastDescriptionElement;
223
		}else if (feature.equals(Feature.COMMON_NAME()) && (isFreetext == null || !isFreetext)){
224
			List<DescriptionElementBase> commonNames = makeCommonNameString(state, reader, next);
225
			//NODE: we do also have the old version makeVernacular, which was called from "others" below
226
			for (DescriptionElementBase commonName : commonNames){
227
				taxonDescription.addElement(commonName);
228
				lastDescriptionElement = commonName;
229
			}
230
			return lastDescriptionElement;
231
		}
232
		else{
233
		
234
			//others
235
			Map<String, String> subheadingMap = handleString(state, reader, next, feature);
236
			for (String subheading : subheadingMap.keySet()) {
237
				Feature subheadingFeature = feature;
238
				if (StringUtils.isNotBlank(subheading) && subheadingMap.size() > 1) {
239
					subheadingFeature = makeFeature(subheading, state, next, null);
240
				}
241
				if (feature.equals(Feature.COMMON_NAME()) && (isFreetext == null || !isFreetext)){
242
					//NOTE: see above 
243
//					List<DescriptionElementBase> commonNames = makeVernacular(state, subheading, subheadingMap.get(subheading));
244
//					for (DescriptionElementBase commonName : commonNames){
245
//						taxonDescription.addElement(commonName);
246
//						lastDescriptionElement = commonName;
247
//					}
248
				}else {
249
					TextData textData = TextData.NewInstance(subheadingFeature);
250
					textData.putText(getDefaultLanguage(state), subheadingMap.get(subheading));
251
					taxonDescription.addElement(textData);
252
					lastDescriptionElement = textData;
253
					// TODO how to handle figures when these data are split in
254
					// subheadings
255
				}
256
			}
257
			return lastDescriptionElement;
258
		}
259
	}
260

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

    
280
			
281
			//get existing feature
282
			Feature feature = state.getTransformer().getFeatureByKey(classValue);
283
			if (feature != null) {
284
				return feature;
285
			}
286
			uuid = state.getTransformer().getFeatureUuid(classValue);
287
			
288
			if (uuid == null){
289
				uuid = state.getUnknownFeatureUuid(classValue);
290
			}
291
			
292
			if (uuid == null) {
293
				// TODO
294
				String message = "Uuid is not defined for '%s'";
295
				message = String.format(message, classValue);
296
				if (! message.contains("<")){
297
					//log only top level features
298
					fireWarningEvent(message, parentEvent, 8);
299
				}
300
				uuid = UUID.randomUUID();
301
				state.putUnknownFeatureUuid(classValue, uuid);
302
			}
303

    
304
			// TODO eFlora vocabulary
305
			TermVocabulary<Feature> voc = null;
306
			feature = getFeature(state, uuid, featureText, featureText, classValue, voc);
307
			if (parentFeature != null){
308
				parentFeature.addIncludes(feature);
309
				save(parentFeature, state);
310
			}
311
			save(feature, state);
312
					
313
			if (feature == null) {
314
				throw new NullPointerException(classValue + " not recognized as a feature");
315
			}
316
//			state.putFeatureToCurrentList(feature);
317
			return feature;
318
		} catch (Exception e) {
319
			String message = "Could not create feature for %s: %s";
320
			message = String.format(message, classValue, e.getMessage());
321
			fireWarningEvent(message, parentEvent, 4);
322
			state.putUnknownFeatureUuid(classValue, null);
323
//			e.printStackTrace();
324
			return Feature.UNKNOWN();
325
		}
326
	}
327
	
328
	public class CharOrder{
329
		static final int strlength = 3;
330
		private int order = 1;
331
		private CharOrder parent;
332
		private List<CharOrder> children = new ArrayList<CharOrder>();
333
		
334
		public CharOrder nextChild(){
335
			CharOrder result = new CharOrder();
336
			if (! children.isEmpty()) {
337
				result.order = children.get(children.size() - 1).order + 1;
338
			}
339
			result.parent = this;
340
			children.add(result);
341
			return result;
342
		}
343
		
344
		public CharOrder next(){
345
			CharOrder result = new CharOrder();
346
			result.order = order + 1;
347
			result.parent = parent;
348
			if (parent != null){
349
				parent.children.add(result);
350
			}
351
			return result;
352
		}
353
		
354
		public String orderString(){
355
			String parentString = parent == null ? "" : parent.orderString();
356
			String result = CdmUtils.concat("-", parentString, StringUtils.leftPad(String.valueOf(order), strlength, '0'));
357
			return result;
358
		}
359
		
360
		public String toString(){
361
			return orderString();
362
		}
363
	}
364
	
365

    
366
	/**
367
	 * Handle the char or subchar element. As 
368
	 * @param state the import state
369
	 * @param reader 
370
	 * @param parentEvent
371
	 * @param parentFeature in case of subchars we need to attache the newly created feature to a parent feature, should be <code>null</code>
372
	 * for top level chars.  
373
	 * @return List of TextData. Not a single one as the recursive TextData will also be returned
374
	 * @throws XMLStreamException
375
	 */
376
	private List<TextData> handleChar(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, Feature parentFeature, CharOrder myCharOrder) throws XMLStreamException {
377
		List<TextData> result = new ArrayList<TextData>();
378
		String classValue = getClassOnlyAttribute(parentEvent);
379
		Feature feature = makeFeature(classValue, state, parentEvent, parentFeature);
380
		if(parentFeature == null){
381
			state.putFeatureToCharSorterList(feature);
382
		}else{
383
			FeatureSorterInfo parentInfo = state.getLatestCharFeatureSorterInfo();
384
//			if (! parentInfo.getUuid().equals(parentFeature.getUuid())){
385
//				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";
386
//				fireWarningEvent(message, parentEvent, 6);
387
//			}else{
388
				state.getLatestCharFeatureSorterInfo().addSubFeature(new FeatureSorterInfo(feature));
389
//			}
390
		}
391
		
392
		TextData textData = TextData.NewInstance(feature);
393
		result.add(textData);
394
		
395
		AnnotationType annType = getAnnotationType(state, MarkupTransformer.uuidOriginalOrder, "Original order", "Order in original treatment", null, AnnotationType.TECHNICAL().getVocabulary());
396
		textData.addAnnotation(Annotation.NewInstance(myCharOrder.orderString(), annType, Language.ENGLISH()));
397
		
398
		boolean isTextMode = true;
399
		String text = "";
400
		while (reader.hasNext()) {
401
			XMLEvent next = readNoWhitespace(reader);
402
			if (isMyEndingElement(next, parentEvent)) {
403
				text = text.trim();
404
				textData.putText(getDefaultLanguage(state), text);
405
				return result;
406
			} else if (isStartingElement(next, FIGURE_REF)) {
407
				//TODO
408
				handleNotYetImplementedElement(next);
409
			} else if (isStartingElement(next, FOOTNOTE_REF)) {
410
				//TODO
411
				handleNotYetImplementedElement(next);
412
			} else if (isStartingElement(next, BR)) {
413
				text += "<br/>";
414
				isTextMode = false;
415
			} else if (isEndingElement(next, BR)) {
416
				isTextMode = true;
417
			} else if (isHtml(next)) {
418
				text += getXmlTag(next);
419
			} else if (next.isStartElement()) {
420
				if (isStartingElement(next, ANNOTATION)) {
421
					handleNotYetImplementedElement(next); //TODO test handleSimpleAnnotation
422
				} else if (isStartingElement(next, ITALICS)) {
423
					handleNotYetImplementedElement(next);
424
				} else if (isStartingElement(next, BOLD)) {
425
					handleNotYetImplementedElement(next);
426
				} else if (isStartingElement(next, FIGURE)) {
427
					handleFigure(state, reader, next, specimenImport, nomenclatureImport);
428
				} else if (isStartingElement(next, SUB_CHAR)) {
429
					List<TextData> subTextData = handleChar(state, reader, next, feature, myCharOrder.nextChild());
430
					result.addAll(subTextData);
431
				} else if (isStartingElement(next, FOOTNOTE)) {
432
					FootnoteDataHolder footnote = handleFootnote(state, reader,	next, specimenImport, nomenclatureImport);
433
					if (footnote.isRef()) {
434
						String message = "Ref footnote not implemented here";
435
						fireWarningEvent(message, next, 4);
436
					} else {
437
						registerGivenFootnote(state, footnote);
438
					}
439
				} else {
440
					handleUnexpectedStartElement(next.asStartElement());
441
				}
442
			} else if (next.isCharacters()) {
443
				if (!isTextMode) {
444
					String message = "String is not in text mode";
445
					fireWarningEvent(message, next, 6);
446
				} else {
447
					text += next.asCharacters().getData();
448
				}
449
			} else {
450
				handleUnexpectedEndElement(next.asEndElement());
451
			}
452
		}
453
		throw new IllegalStateException("RefPart has no closing tag");
454
	}
455
	
456

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

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

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

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

    
540
				return;
541
			} else if (next.isStartElement()) {
542
				if (isStartingElement(next, ALTITUDE)) {
543
					text = text.trim() + getTaggedCData(state, reader, next);
544
				} else if (isStartingElement(next, LIFE_CYCLE_PERIODS)) {
545
					handleNotYetImplementedElement(next);
546
				} else {
547
					handleUnexpectedStartElement(next.asStartElement());
548
				}
549
			} else if (next.isCharacters()) {
550
				text += next.asCharacters().getData();
551
			} else {
552
				handleUnexpectedElement(next);
553
			}
554
		}
555
		throw new IllegalStateException("<Habitat> has no closing tag");
556
	}
557
	
558

    
559
	private FigureDataHolder handleFigureRef(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent)
560
			throws XMLStreamException {
561
		FigureDataHolder result = new FigureDataHolder();
562
		Map<String, Attribute> attributes = getAttributes(parentEvent);
563
		result.ref = getAndRemoveAttributeValue(attributes, REF);
564
		checkNoAttributes(attributes, parentEvent);
565

    
566
		// text is not handled, needed only for debugging purposes
567
		String text = "";
568
		while (reader.hasNext()) {
569
			XMLEvent next = readNoWhitespace(reader);
570
			if (isMyEndingElement(next, parentEvent)) {
571
				return result;
572
			} else if (isStartingElement(next, NUM)) {
573
				String num = getCData(state, reader, next);
574
				result.num = num; // num is not handled during import
575
			} else if (isStartingElement(next, FIGURE_PART)) {
576
				result.figurePart = getCData(state, reader, next);
577
			} else if (next.isCharacters()) {
578
				text += next.asCharacters().getData();
579
			} else {
580
				fireUnexpectedEvent(next, 0);
581
			}
582
		}
583
		throw new IllegalStateException("<figureRef> has no end tag");
584
	}
585

    
586

    
587
	private void registerFigureDemand(MarkupImportState state, XMLEvent next, AnnotatableEntity entity, String figureRef) {
588
		Media existingFigure = state.getFigure(figureRef);
589
		if (existingFigure != null) {
590
			attachFigure(state, next, entity, existingFigure);
591
		} else {
592
			Set<AnnotatableEntity> demands = state.getFigureDemands(figureRef);
593
			if (demands == null) {
594
				demands = new HashSet<AnnotatableEntity>();
595
				state.putFigureDemands(figureRef, demands);
596
			}
597
			demands.add(entity);
598
		}
599
	}
600
	
601
	private List<DescriptionElementBase> makeCommonNameString(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException{
602
		
603
		List<DescriptionElementBase> result = new ArrayList<DescriptionElementBase>();
604

    
605
		checkNoAttributes(parentEvent);
606
		
607
		while (reader.hasNext()) {
608
			XMLEvent next = readNoWhitespace(reader);
609
			if (isMyEndingElement(next, parentEvent)) {
610
				if (result.isEmpty()){
611
					fireWarningEvent("Common name was not created", next, 4);
612
				}
613
				return result;
614
			} else if (isStartingElement(next, VERNACULAR_NAMES)) {
615
				result = makeVernacularNames(state, reader, next);
616
			} else if (isStartingElement(next, SUB_HEADING)) {
617
				String subheading = getCData(state, reader, next);
618
				if (! subheading.matches("Nom(s)? vernaculaire(s)?\\:")){
619
					fireWarningEvent("Subheading for vernacular name not recognized: " + subheading, next, 4);
620
				}
621
			} else if (next.isCharacters()) {
622
				String chars = next.asCharacters().toString().trim();
623
				if (chars.equals(".")){
624
					//do nothing
625
				}else{
626
					fireWarningEvent("Character not handled in vernacular name: " + chars, next, 4);
627
				}
628
			}else {
629
				handleUnexpectedElement(next);
630
			}
631
		}
632
		throw new IllegalStateException("closing tag is missing");
633
		
634
		
635
	}
636

    
637
	private List<DescriptionElementBase> makeVernacularNames(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException{
638
		List<DescriptionElementBase> result = new ArrayList<DescriptionElementBase>();
639
		checkNoAttributes(parentEvent);
640
	
641
		while (reader.hasNext()) {
642
			XMLEvent next = readNoWhitespace(reader);
643
			if (isMyEndingElement(next, parentEvent)) {
644
				state.removeCurrentAreas();
645
				return result;
646
			} else if (isStartingElement(next, VERNACULAR_NAME)) {
647
				List<CommonTaxonName> names = makeSingleVernacularName(state, reader, next);
648
				result.addAll(names);
649
			} else if (isStartingElement(next, SUB_HEADING)) {
650
				makeVernacularNamesSubHeading(state, reader, next);
651
			} else {
652
				handleUnexpectedElement(next);
653
			}
654
		}
655
		throw new IllegalStateException("closing tag is missing");
656
		
657
	}
658
	
659
	private void makeVernacularNamesSubHeading(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
660
		checkNoAttributes(parentEvent);
661
		
662
		String text = "";
663
		while (reader.hasNext()) {
664
			XMLEvent next = readNoWhitespace(reader);
665
			if (isMyEndingElement(next, parentEvent)) {
666
				if (StringUtils.isNotBlank(text)){
667
					NamedArea area = getCommonNameArea(text);
668
					if (area != null){
669
						state.removeCurrentAreas();
670
						state.addCurrentArea(area);
671
					}else{
672
						fireWarningEvent("Vernacular subheading not recognized", next, 8);
673
					}
674
				}
675
				
676
				return ;
677
			} else if (next.isCharacters()) {
678
				text += next.asCharacters().getData();
679
			} else {
680
				handleUnexpectedElement(next);
681
			}
682
		}
683
		throw new IllegalStateException("closing tag is missing");
684
		
685
	}
686

    
687
	private NamedArea getCommonNameArea(String text) {
688
		if (text.endsWith(":")){
689
			text = text.substring(0, text.length()-1);
690
		}
691
		
692
		// for now we do it hardcoded
693
		if (text.equalsIgnoreCase("Guyana")){
694
			return Country.GUYANAREPUBLICOF();
695
		}else if (text.equalsIgnoreCase("Suriname")){
696
			return Country.SURINAMEREPUBLICOF();
697
		}else if (text.equalsIgnoreCase("French Guiana")){
698
			return Country.FRENCHGUIANA();
699
		}
700
		return null;
701
	}
702

    
703
	private List<CommonTaxonName> makeSingleVernacularName(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException{
704
		checkNoAttributes(parentEvent);
705
		List<CommonTaxonName> result = new ArrayList<CommonTaxonName>();
706
				
707
		Language language = state.getDefaultLanguage();
708
		while (reader.hasNext()) {
709
			XMLEvent next = readNoWhitespace(reader);
710
			if (isMyEndingElement(next, parentEvent)) {
711
				for (CommonTaxonName commonName : result){
712
					commonName.setLanguage(language);
713
				}
714
//				if (isNotBlank(name)){
715
//					result.setName(name);
716
//				}else{
717
//					fireWarningEvent("No name string for common name", parentEvent, 4);
718
//				}
719
				
720
				return result;
721
			} else if (isStartingElement(next, NAME)) {
722
				//TODO test
723
				CommonTaxonName name = handleVernacularNameName(state, reader, next);
724
				if (name != null){
725
					result.add(name);
726
				}
727
			} else if (isStartingElement(next, LOCAL_LANGUAGE)) {
728
				Language localLanguage = handleLocalLanguage(state, reader, next);
729
				if (localLanguage != null){
730
					language = localLanguage;
731
				}
732
			} else if (isStartingElement(next, TRANSLATION)) {
733
				//TODO
734
				handleNotYetImplementedElement(next);
735
			} else if (isStartingElement(next, LOCALITY)) {
736
				//TODO
737
				handleNotYetImplementedElement(next);
738
			} else if (isStartingElement(next, ANNOTATION)){
739
				//TODO
740
				handleNotYetImplementedElement(next);
741
			} else if (isStartingElement(next, FOOTNOTE_REF)) {
742
				//TODO
743
				handleNotYetImplementedElement(next);
744
			} else if (next.isCharacters()) {
745
				String chars = next.asCharacters().toString().trim();
746
				if (chars.equals("(") || chars.equals(")") || chars.equals(",")){
747
					//do nothing
748
				}else{
749
					fireWarningEvent("Character not handled in vernacular name: " + chars, next, 4);
750
				}
751
			} else {
752
				handleUnexpectedElement(next);
753
			}
754
		}
755
		throw new IllegalStateException("closing tag is missing");
756
	}
757
	
758
	private CommonTaxonName handleVernacularNameName(MarkupImportState state, XMLEventReader reader, 
759
				XMLEvent parentEvent) throws XMLStreamException {
760
		//attributes
761
		Map<String, Attribute> attributes = getAttributes(parentEvent);
762
		this.checkAndRemoveAttributeValue(attributes, CLASS, "vernacular");
763
		this.checkNoAttributes(attributes, parentEvent);
764
		
765
		//
766
		String text = getCData(state, reader, parentEvent, false);
767
		CommonTaxonName name = CommonTaxonName.NewInstance(text, null);
768
		if (! state.getCurrentAreas().isEmpty()){
769
			if (state.getCurrentAreas().size() > 1){
770
				fireWarningEvent("Multiple areas for common name not yet covered by CDM", parentEvent , 8);
771
			}else{
772
				name.setArea(state.getCurrentAreas().iterator().next());
773
			}
774
		}
775
		return name;
776
	}
777

    
778
	private Language handleLocalLanguage(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
779
		//attributes
780
		Map<String, Attribute> attributes = getAttributes(parentEvent);
781
		boolean doubtful = getAndRemoveBooleanAttributeValue(parentEvent, attributes, DOUBTFUL, false);
782
		boolean unknown = getAndRemoveBooleanAttributeValue(parentEvent, attributes, UNKNOWN, false);
783
		this.checkNoAttributes(attributes, parentEvent);
784
		
785
		if (doubtful == true){
786
			fireWarningEvent("Doubtful not yet implemented for local language", parentEvent, 2);
787
		}
788
		if (unknown == true){
789
			fireWarningEvent("Unknown not yet implemented for local language ", parentEvent, 2);
790
		}
791
		
792
		//
793
		String text = getCData(state, reader, parentEvent);
794
		Language lang = makeLanguageByLangStr(state, text);
795
		return lang;
796

    
797
	}
798

    
799
	private List<DescriptionElementBase> makeVernacular(MarkupImportState state, String subheading, String commonNameString) throws XMLStreamException {
800
		List<DescriptionElementBase> result = new ArrayList<DescriptionElementBase>();
801
		String[] splits = commonNameString.split(",");
802
		for (String split : splits){
803
			split = split.trim();
804
			if (! split.matches(".*\\(.*\\)\\.?")){
805
				fireWarningEvent("Common name string '"+split+"' does not match given pattern", state.getReader().peek(), 4);
806
			}
807
			
808
			String name = split.replaceAll("\\(.*\\)", "").replace(".", "").trim();
809
			String languageStr = split.replaceFirst(".*\\(", "").replaceAll("\\)\\.?", "").trim();
810
			
811
			Language language = null;
812
			if (StringUtils.isNotBlank(languageStr)){
813
				language = makeLanguageByLangStr(state, languageStr);
814
			}
815
			DescriptionElementBase commonName;
816
			if (name != null && name.length() < 255 ){
817
				NamedArea area = null;
818
				commonName = CommonTaxonName.NewInstance(name, language, area);
819
			}else{
820
				if (language == null){
821
					language = getDefaultLanguage(state);
822
				}
823
				commonName = TextData.NewInstance(Feature.COMMON_NAME(), name, language, null);
824
				String warning = "Vernacular feature is >255 size. Therefore it is handled as TextData, not CommonTaxonName: " + name;
825
				fireWarningEvent(warning, state.getReader().peek(), 1);
826
			}
827
			result.add(commonName);
828
		}
829
		
830
		return result;
831
	}
832

    
833
	private Language makeLanguageByLangStr(MarkupImportState state, String languageStr) throws XMLStreamException {
834
		try {
835
			Language language = state.getTransformer().getLanguageByKey(languageStr);
836
			if (language == null){
837
				UUID langUuid = state.getTransformer().getLanguageUuid(languageStr);
838
				TermVocabulary<?> voc = null;
839
				language = getLanguage(state, langUuid, languageStr, languageStr, null, voc);
840
			}	
841
			if (language == null){
842
				String warning = "Language " + languageStr + " not recognized by transformer";
843
				fireWarningEvent(warning, state.getReader().peek(), 4);
844
			}
845
			return language;
846
		} catch (UndefinedTransformerMethodException e) {
847
			throw new RuntimeException(e);
848
		}
849
	}
850

    
851

    
852
	private String handleHeading(MarkupImportState state,XMLEventReader reader, XMLEvent parentEvent)throws XMLStreamException {
853
		checkNoAttributes(parentEvent);
854

    
855
		String text = "";
856
		while (reader.hasNext()) {
857
			XMLEvent next = readNoWhitespace(reader);
858
			if (isMyEndingElement(next, parentEvent)) {
859
				return text;
860
			} else if (next.isStartElement()) {
861
				if (isStartingElement(next, FOOTNOTE)) {
862
					handleNotYetImplementedElement(next);
863
				} else {
864
					handleUnexpectedStartElement(next.asStartElement());
865
				}
866
			} else if (next.isCharacters()) {
867
				text += next.asCharacters().getData();
868
			} else {
869
				handleUnexpectedEndElement(next.asEndElement());
870
			}
871
		}
872
		throw new IllegalStateException("<String> has no closing tag");
873

    
874
	}
875
	
876

    
877
	private List<Reference<?>> handleReferences(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
878
		// attributes
879
		Map<String, Attribute> attributes = getAttributes(parentEvent);
880
		String bibliography = getAndRemoveAttributeValue(attributes,
881
				BIBLIOGRAPHY);
882
		String serialsAbbreviations = getAndRemoveAttributeValue(attributes,
883
				SERIALS_ABBREVIATIONS);
884
		if (isNotBlank(bibliography) || isNotBlank(serialsAbbreviations)) {
885
			String message = "Attributes not yet implemented for <references>";
886
			fireWarningEvent(message, parentEvent, 4);
887
		}
888

    
889
		List<Reference<?>> result = new ArrayList<Reference<?>>();
890

    
891
		// elements
892
		while (reader.hasNext()) {
893
			XMLEvent next = readNoWhitespace(reader);
894
			if (next.isEndElement()) {
895
				if (isMyEndingElement(next, parentEvent)) {
896
					return result;
897
				} else {
898
					if (isEndingElement(next, HEADING)) {
899
						// NOT YET IMPLEMENTED
900
						popUnimplemented(next.asEndElement());
901
					} else if (isEndingElement(next, WRITER)) {
902
						// NOT YET IMPLEMENTED
903
						popUnimplemented(next.asEndElement());
904
					} else if (isEndingElement(next, FOOTNOTE)) {
905
						// NOT YET IMPLEMENTED
906
						popUnimplemented(next.asEndElement());
907
					} else if (isEndingElement(next, STRING)) {
908
						// NOT YET IMPLEMENTED
909
						popUnimplemented(next.asEndElement());
910
					} else if (isEndingElement(next, REF_NUM)) {
911
						// NOT YET IMPLEMENTED
912
						popUnimplemented(next.asEndElement());
913
					} else {
914
						handleUnexpectedEndElement(next.asEndElement());
915
					}
916
				}
917
			} else if (next.isStartElement()) {
918
				if (isStartingElement(next, HEADING)) {
919
					handleNotYetImplementedElement(next);
920
				} else if (isStartingElement(next, SUB_HEADING)) {
921
					String subheading = getCData(state, reader, next).trim();
922
					String excludePattern = "(i?)(References?|Literature):?";
923
					if (!subheading.matches(excludePattern)) {
924
						fireNotYetImplementedElement(next.getLocation(), next.asStartElement().getName(), 0);
925
					}
926
				} else if (isStartingElement(next, WRITER)) {
927
					handleNotYetImplementedElement(next);
928
				} else if (isStartingElement(next, FOOTNOTE)) {
929
					handleNotYetImplementedElement(next);
930
				} else if (isStartingElement(next, STRING)) {
931
					handleNotYetImplementedElement(next);
932
				} else if (isStartingElement(next, REF_NUM)) {
933
					handleNotYetImplementedElement(next);
934
				} else if (isStartingElement(next, REFERENCE)) {
935
					Reference<?> ref = nomenclatureImport.handleReference(state, reader, next);
936
					result.add(ref);
937
				} else {
938
					handleUnexpectedStartElement(next);
939
				}
940
			} else {
941
				handleUnexpectedElement(next);
942
			}
943
		}
944
		throw new IllegalStateException("<References> has no closing tag");
945
	}
946
	
947

    
948
	private String getTaggedCData(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
949
		checkNoAttributes(parentEvent);
950

    
951
		String text = getXmlTag(parentEvent);
952
		while (reader.hasNext()) {
953
			XMLEvent next = readNoWhitespace(reader);
954
			if (isMyEndingElement(next, parentEvent)) {
955
				text += getXmlTag(next);
956
				return text;
957
			} else if (next.isStartElement()) {
958
				text += getTaggedCData(state, reader, next);
959
			} else if (next.isEndElement()) {
960
				text += getTaggedCData(state, reader, next);
961
			} else if (next.isCharacters()) {
962
				text += next.asCharacters().getData();
963
			} else {
964
				handleUnexpectedEndElement(next.asEndElement());
965
			}
966
		}
967
		throw new IllegalStateException("Some tag has no closing tag");
968
	}
969
}
(8-8/19)