Project

General

Profile

Download (29.1 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.HashMap;
13
import java.util.Map;
14

    
15
import javax.xml.stream.XMLEventReader;
16
import javax.xml.stream.XMLStreamException;
17
import javax.xml.stream.events.Attribute;
18
import javax.xml.stream.events.StartElement;
19
import javax.xml.stream.events.XMLEvent;
20

    
21
import org.apache.commons.lang.StringUtils;
22
import org.apache.log4j.Logger;
23

    
24
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
25
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
26
import eu.etaxonomy.cdm.model.common.CdmBase;
27
import eu.etaxonomy.cdm.model.common.TimePeriod;
28
import eu.etaxonomy.cdm.model.description.Feature;
29
import eu.etaxonomy.cdm.model.description.TaxonDescription;
30
import eu.etaxonomy.cdm.model.description.TextData;
31
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
32
import eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus;
33
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
34
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
35
import eu.etaxonomy.cdm.model.name.NonViralName;
36
import eu.etaxonomy.cdm.model.name.Rank;
37
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
38
import eu.etaxonomy.cdm.model.reference.IArticle;
39
import eu.etaxonomy.cdm.model.reference.IJournal;
40
import eu.etaxonomy.cdm.model.reference.Reference;
41
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
42
import eu.etaxonomy.cdm.model.reference.ReferenceType;
43
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
44
import eu.etaxonomy.cdm.model.taxon.Taxon;
45
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
46
import eu.etaxonomy.cdm.strategy.parser.NameTypeParser;
47
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
48

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

    
58

    
59
	private NonViralNameParserImpl parser = new NonViralNameParserImpl();
60

    
61
	private MarkupKeyImport keyImport;
62
	private MarkupSpecimenImport specimenImport;
63

    
64
	public MarkupNomenclatureImport(MarkupDocumentImport docImport,
65
			MarkupKeyImport keyImport, MarkupSpecimenImport specimenImport) {
66
		super(docImport);
67
		this.keyImport = keyImport;
68
		this.specimenImport = specimenImport;
69
	}
70

    
71
	public void handleNomenclature(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent)
72
			throws XMLStreamException {
73
		checkNoAttributes(parentEvent);
74

    
75
		while (reader.hasNext()) {
76
			XMLEvent next = readNoWhitespace(reader);
77
			if (isStartingElement(next, HOMOTYPES)) {
78
				handleHomotypes(state, reader, next.asStartElement());
79
			} else if (isMyEndingElement(next, parentEvent)) {
80
				return;
81
			} else {
82
				fireSchemaConflictEventExpectedStartTag(HOMOTYPES, reader);
83
				state.setUnsuccessfull();
84
			}
85
		}
86
		return;
87
	}
88

    
89
	private void handleHomotypes(MarkupImportState state,
90
			XMLEventReader reader, StartElement parentEvent)
91
			throws XMLStreamException {
92
		checkNoAttributes(parentEvent);
93

    
94
		HomotypicalGroup homotypicalGroup = null;
95

    
96
		boolean hasNom = false;
97
		while (reader.hasNext()) {
98
			XMLEvent next = readNoWhitespace(reader);
99
			if (next.isEndElement()) {
100
				if (isMyEndingElement(next, parentEvent)) {
101
					checkMandatoryElement(hasNom, parentEvent, NOM);
102
					return;
103
				} else {
104
					if (isEndingElement(next, NAME_TYPE)) {
105
						state.setNameType(false);
106
					} else if (isEndingElement(next, NOTES)) {
107
						// NOT YET IMPLEMENTED
108
						popUnimplemented(next.asEndElement());
109
					} else {
110
						handleUnexpectedEndElement(next.asEndElement());
111
					}
112
				}
113
			} else if (next.isStartElement()) {
114
				if (isStartingElement(next, NOM)) {
115
					NonViralName<?> name = handleNom(state, reader, next, homotypicalGroup);
116
					homotypicalGroup = name.getHomotypicalGroup();
117
					hasNom = true;
118
				} else if (isStartingElement(next, NAME_TYPE)) {
119
					state.setNameType(true);
120
					handleNameType(state, reader, next, homotypicalGroup);
121
				} else if (isStartingElement(next, SPECIMEN_TYPE)) {
122
					specimenImport.handleSpecimenType(state, reader, next,
123
							homotypicalGroup);
124
				} else if (isStartingElement(next, NOTES)) {
125
					handleNotYetImplementedElement(next);
126
				} else {
127
					handleUnexpectedStartElement(next);
128
				}
129
			} else {
130
				handleUnexpectedElement(next);
131
			}
132
		}
133
		// TODO handle missing end element
134
		throw new IllegalStateException("Homotypes has no closing tag");
135

    
136
	}
137

    
138
	private void handleNameType(MarkupImportState state, XMLEventReader reader,
139
			XMLEvent parentEvent, HomotypicalGroup homotypicalGroup)
140
			throws XMLStreamException {
141
		Map<String, Attribute> attributes = getAttributes(parentEvent);
142
		String typeStatus = getAndRemoveAttributeValue(attributes, TYPE_STATUS);
143
		checkNoAttributes(attributes, parentEvent);
144

    
145
		NameTypeDesignationStatus status;
146
		try {
147
			status = NameTypeParser.parseNameTypeStatus(typeStatus);
148
		} catch (UnknownCdmTypeException e) {
149
			String message = "Type status could not be recognized: %s";
150
			message = String.format(message, typeStatus);
151
			fireWarningEvent(message, parentEvent, 4);
152
			status = null;
153
		}
154

    
155
		boolean hasNom = false;
156
		while (reader.hasNext()) {
157
			XMLEvent next = readNoWhitespace(reader);
158
			if (next.isEndElement()) {
159
				if (isMyEndingElement(next, parentEvent)) {
160
					checkMandatoryElement(hasNom, parentEvent.asStartElement(),
161
							NOM);
162
					state.setNameType(false);
163
					return;
164
				} else {
165
					if (isEndingElement(next, ACCEPTED_NAME)) {
166
						// NOT YET IMPLEMENTED
167
						popUnimplemented(next.asEndElement());
168
					} else {
169
						handleUnexpectedEndElement(next.asEndElement());
170
					}
171
				}
172
			} else if (next.isStartElement()) {
173
				if (isStartingElement(next, NOM)) {
174
					// TODO should we check if the type is always a species, is
175
					// this a rule?
176
					NonViralName<?> speciesName = handleNom(state, reader,
177
							next, null);
178
					for (TaxonNameBase<?, ?> name : homotypicalGroup
179
							.getTypifiedNames()) {
180
						name.addNameTypeDesignation(speciesName, null, null,
181
								null, status, false, false, false, false);
182
					}
183
					hasNom = true;
184
				} else if (isStartingElement(next, ACCEPTED_NAME)) {
185
					handleNotYetImplementedElement(next);
186
				} else {
187
					handleUnexpectedStartElement(next);
188
				}
189
			} else {
190
				handleUnexpectedElement(next);
191
			}
192
		}
193
		// TODO handle missing end element
194
		throw new IllegalStateException("Homotypes has no closing tag");
195

    
196
	}
197

    
198
	/**
199
	 * Creates the name defined by a nom tag. Adds it to the given homotypical
200
	 * group (if not null).
201
	 * 
202
	 * @param state
203
	 * @param reader
204
	 * @param parentEvent
205
	 * @param homotypicalGroup
206
	 * @return
207
	 * @throws XMLStreamException
208
	 */
209
	private NonViralName<?> handleNom(MarkupImportState state,
210
			XMLEventReader reader, XMLEvent parentEvent,
211
			HomotypicalGroup homotypicalGroup) throws XMLStreamException {
212
		boolean isSynonym = false;
213
		boolean isNameType = state.isNameType();
214
		// attributes
215
		String classValue = getClassOnlyAttribute(parentEvent);
216
		NonViralName<?> name;
217
		if (!isNameType && ACCEPTED.equalsIgnoreCase(classValue)) {
218
			isSynonym = false;
219
			name = createName(state, homotypicalGroup, isSynonym);
220
		} else if (!isNameType && SYNONYM.equalsIgnoreCase(classValue)) {
221
			isSynonym = true;
222
			name = createName(state, homotypicalGroup, isSynonym);
223
		} else if (isNameType && NAME_TYPE.equalsIgnoreCase(classValue)) {
224
			// TODO do we need to define the rank here?
225
			name = createNameByCode(state, null);
226
		} else {
227
			fireUnexpectedAttributeValue(parentEvent, CLASS, classValue);
228
			name = createNameByCode(state, null);
229
		}
230

    
231
		Map<String, String> nameMap = new HashMap<String, String>();
232
		String text = "";
233
		
234
		while (reader.hasNext()) {
235
			XMLEvent next = readNoWhitespace(reader);
236
			if (isMyEndingElement(next, parentEvent)) {
237
				// fill the name with all data gathered
238
				fillName(state, nameMap, name, next);
239
				handleNomText(state, parentEvent, text, isNameType);
240
				return name;
241
			} else if (isEndingElement(next, ANNOTATION)) {
242
				// NOT YET IMPLEMENTED //TODO test
243
				// handleSimpleAnnotation
244
				popUnimplemented(next.asEndElement());
245
			}else if (isStartingElement(next, FULL_NAME)) {
246
				handleFullName(state, reader, name, next);
247
			} else if (isStartingElement(next, NUM)) {
248
				handleNomNum(state, reader, next);
249
			} else if (isStartingElement(next, NAME)) {
250
				handleName(state, reader, next, nameMap);
251
			} else if (isStartingElement(next, CITATION)) {
252
				handleCitation(state, reader, next, name);
253
			} else if (next.isCharacters()) {
254
				text += next.asCharacters().getData();
255
			} else if (isStartingElement(next, HOMONYM)) {
256
				handleNotYetImplementedElement(next);
257
			} else if (isStartingElement(next, NOTES)) {
258
				handleNotYetImplementedElement(next);
259
			} else if (isStartingElement(next, ANNOTATION)) {
260
				handleNotYetImplementedElement(next);
261
			} else {
262
				handleUnexpectedElement(next);
263
			}
264
		}
265
		// TODO handle missing end element
266
		throw new IllegalStateException("Nom has no closing tag");
267
	}
268

    
269
	/**
270
	 * Handles appearance of text within <nom> tags.
271
	 * Usually this is not expected except for some information that is already handled
272
	 * elsewhere, e.g. the string Nametype is holding information that is available already
273
	 * via the surrounding nametype tag. Therefore this information can be neglected.
274
	 * This method is open for upcoming cases which need to be handled. 
275
	 * @param state
276
	 * @param event
277
	 * @param text
278
	 * @param isNameType
279
	 */
280
	private void handleNomText(MarkupImportState state, XMLEvent event, String text, boolean isNameType) {
281
		if (isBlank(text)){
282
			return;
283
		}
284
		text = text.trim();
285
		//neglect known redundant strings
286
		if (isNameType && text.matches("(?i)^Esp[\u00E8\u00C8]ce[·\\-\\s]type\\:$")){
287
			return;
288
		}//neglect meaningless punctuation
289
		else if (isPunctuation(text)){
290
			return;	
291
		}else{
292
			String message = "Unhandled text in <nom> tag: \"%s\"";
293
			fireWarningEvent(String.format(message, text), event, 4);
294
		}
295
	}
296
	
297
	/**
298
	 * @param state
299
	 * @param reader
300
	 * @param next
301
	 * @throws XMLStreamException
302
	 */
303
	private void handleNomNum(MarkupImportState state, XMLEventReader reader,
304
			XMLEvent next) throws XMLStreamException {
305
		String num = getCData(state, reader, next);
306
		num = num.replace(".", "");
307
		num = num.replace(")", "");
308
		if (StringUtils.isNotBlank(num)) {
309
			if (state.getCurrentTaxonNum() != null
310
					&& !state.getCurrentTaxonNum().equals(num)) {
311
				String message = "Taxontitle num and homotypes/nom/num differ ( %s <-> %s ). I use the later one.";
312
				message = String.format(message,
313
						state.getCurrentTaxonNum(), num);
314
				fireWarningEvent(message, next, 4);
315
			}
316
			state.setCurrentTaxonNum(num);
317
		}
318
	}
319

    
320
	/**
321
	 * @param state
322
	 * @param reader
323
	 * @param name
324
	 * @param next
325
	 * @throws XMLStreamException
326
	 */
327
	private void handleFullName(MarkupImportState state, XMLEventReader reader,
328
			NonViralName<?> name, XMLEvent next) throws XMLStreamException {
329
		String fullNameStr;
330
		Map<String, Attribute> attrs = getAttributes(next);
331
		String rankStr = getAndRemoveRequiredAttributeValue(next,
332
				attrs, "rank");
333
		Rank rank = makeRank(state, rankStr, false);
334
		name.setRank(rank);
335
		if (rank == null) {
336
			String message = "Rank was computed as null. This must not be.";
337
			fireWarningEvent(message, next, 6);
338
			name.setRank(Rank.UNKNOWN_RANK());
339
		}
340
		if (!attrs.isEmpty()) {
341
			handleUnexpectedAttributes(next.getLocation(), attrs);
342
		}
343
		fullNameStr = getCData(state, reader, next);
344
		name.setTitleCache(fullNameStr, true);
345
	}
346

    
347
	private void handleName(MarkupImportState state, XMLEventReader reader,
348
			XMLEvent parentEvent, Map<String, String> nameMap)
349
			throws XMLStreamException {
350
		String classValue = getClassOnlyAttribute(parentEvent);
351

    
352
		String text = "";
353
		while (reader.hasNext()) {
354
			XMLEvent next = readNoWhitespace(reader);
355
			if (isMyEndingElement(next, parentEvent)) {
356
				nameMap.put(classValue, text);
357
				return;
358
			} else if (isStartingElement(next, ANNOTATION)) {
359
				handleNotYetImplementedElement(next); // TODO test
360
														// handleSimpleAnnotation
361
			} else if (next.isCharacters()) {
362
				text += next.asCharacters().getData();
363
			} else {
364
				handleUnexpectedElement(next);
365
			}
366
		}
367
		throw new IllegalStateException("name has no closing tag");
368
	}
369

    
370
	private void fillName(MarkupImportState state, Map<String, String> nameMap,
371
			NonViralName<?> name, XMLEvent event) {
372

    
373
		// Ranks: family, subfamily, tribus, genus, subgenus, section,
374
		// subsection, species, subspecies, variety, subvariety, forma
375
		// infrank, paraut, author, infrparaut, infraut, status, notes
376

    
377
		String infrank = getAndRemoveMapKey(nameMap, INFRANK);
378
		String authorStr = getAndRemoveMapKey(nameMap, AUTHOR);
379
		String paraut = getAndRemoveMapKey(nameMap, PARAUT);
380

    
381
		String infrParAut = getAndRemoveMapKey(nameMap, INFRPARAUT);
382
		String infrAut = getAndRemoveMapKey(nameMap, INFRAUT);
383

    
384
		String statusStr = getAndRemoveMapKey(nameMap, STATUS);
385
		String notes = getAndRemoveMapKey(nameMap, NOTES);
386

    
387
		if (!name.isProtectedTitleCache()) { // otherwise fullName
388

    
389
			makeRankDecision(state, nameMap, name, event, infrank);
390

    
391
			// test consistency of rank and authors
392
			testRankAuthorConsistency(name, event, authorStr, paraut,
393
					infrParAut, infrAut);
394

    
395
			// authors
396
			makeNomenclaturalAuthors(name, event, authorStr, paraut,
397
					infrParAut, infrAut);
398
		}
399

    
400
		// status
401
		// TODO handle pro parte, pro syn. etc.
402
		if (StringUtils.isNotBlank(statusStr)) {
403
			String proPartePattern = "(pro parte|p.p.)";
404
			if (statusStr.matches(proPartePattern)) {
405
				state.setProParte(true);
406
			}
407
			try {
408
				// TODO handle trim earlier
409
				statusStr = statusStr.trim();
410
				NomenclaturalStatusType nomStatusType = NomenclaturalStatusType
411
						.getNomenclaturalStatusTypeByAbbreviation(statusStr);
412
				name.addStatus(NomenclaturalStatus.NewInstance(nomStatusType));
413
			} catch (UnknownCdmTypeException e) {
414
				String message = "Status '%s' could not be recognized";
415
				message = String.format(message, statusStr);
416
				fireWarningEvent(message, event, 4);
417
			}
418
		}
419

    
420
		// notes
421
		if (StringUtils.isNotBlank(notes)) {
422
			handleNotYetImplementedAttributeValue(event, CLASS, NOTES);
423
		}
424

    
425
		return;
426
	}
427

    
428
	/**
429
	 * @param state
430
	 * @param nameMap
431
	 * @param name
432
	 * @param event
433
	 * @param infrankStr
434
	 */
435
	private void makeRankDecision(MarkupImportState state,
436
			Map<String, String> nameMap, NonViralName<?> name, XMLEvent event,
437
			String infrankStr) {
438
		// TODO ranks
439
		for (String key : nameMap.keySet()) {
440
			Rank rank = makeRank(state, key, false);
441
			if (rank == null) {
442
				handleNotYetImplementedAttributeValue(event, CLASS, key);
443
			} else {
444
				if (name.getRank() == null || rank.isLower(name.getRank())) {
445
					name.setRank(rank);
446
				}
447
				String value = nameMap.get(key);
448
				if (rank.isSupraGeneric() || rank.isGenus()) {
449
					if ((key.equalsIgnoreCase(GENUS_ABBREVIATION)
450
							&& isNotBlank(state.getLatestGenusEpithet()) || isGenusAbbrev(
451
								value, state.getLatestGenusEpithet()))) {
452
						value = state.getLatestGenusEpithet();
453
					}
454
					name.setGenusOrUninomial(toFirstCapital(value));
455
				} else if (rank.isInfraGeneric()) {
456
					name.setInfraGenericEpithet(toFirstCapital(value));
457
				} else if (rank.isSpecies()) {
458
					if (state.getConfig().isAllowCapitalSpeciesEpithet()
459
							&& isFirstCapitalWord(value)) { // capital letters
460
															// are allowed for
461
															// species epithet
462
															// in case of person
463
															// names (e.g.
464
															// Manilkara
465
															// Welwitschii Engl.
466
						name.setSpecificEpithet(value);
467
					} else {
468
						name.setSpecificEpithet(value.toLowerCase());
469
					}
470
				} else if (rank.isInfraSpecific()) {
471
					name.setInfraSpecificEpithet(value.toLowerCase());
472
				} else {
473
					String message = "Invalid rank '%s'. Can't decide which epithet to fill with '%s'";
474
					message = String.format(message, rank.getTitleCache(),
475
							value);
476
					fireWarningEvent(message, event, 4);
477
				}
478
			}
479

    
480
		}
481
		// handle given infrank marker
482
		if (StringUtils.isNotBlank(infrankStr)) {
483
			Rank infRank = makeRank(state, infrankStr, true);
484

    
485
			if (infRank == null) {
486
				String message = "Infrank '%s' rank not recognized";
487
				message = String.format(message, infrankStr);
488
				fireWarningEvent(message, event, 4);
489
			} else {
490
				if (name.getRank() == null) {
491
					name.setRank(infRank);
492
				} else if (infRank.isLower(name.getRank())) {
493
					String message = "InfRank '%s' is lower than existing rank ";
494
					message = String.format(message, infrankStr);
495
					fireWarningEvent(message, event, 2);
496
					name.setRank(infRank);
497
				} else if (infRank.equals(name.getRank())) {
498
					// nothing
499
				} else {
500
					String message = "InfRank '%s' is higher than existing rank ";
501
					message = String.format(message, infrankStr);
502
					fireWarningEvent(message, event, 2);
503
				}
504
			}
505
		}
506
	}
507

    
508
	/**
509
	 * @param name
510
	 * @param event
511
	 * @param authorStr
512
	 * @param paraut
513
	 * @param infrParAut
514
	 * @param infrAut
515
	 */
516
	private void makeNomenclaturalAuthors(NonViralName name, XMLEvent event,
517
			String authorStr, String paraut, String infrParAut, String infrAut) {
518
		if (name.getRank() != null && name.getRank().isInfraSpecific()) {
519
			if (StringUtils.isNotBlank(infrAut)) {
520
				INomenclaturalAuthor[] authorAndEx = authorAndEx(infrAut, event);
521
				name.setCombinationAuthorTeam(authorAndEx[0]);
522
				name.setExCombinationAuthorTeam(authorAndEx[1]);
523
			}
524
			if (StringUtils.isNotBlank(infrParAut)) {
525
				INomenclaturalAuthor[] authorAndEx = authorAndEx(infrParAut,
526
						event);
527
				name.setBasionymAuthorTeam(authorAndEx[0]);
528
				name.setExBasionymAuthorTeam(authorAndEx[1]);
529
			}
530
		} else {
531
			if (name.getRank() == null) {
532
				String message = "No rank defined. Check correct usage of authors!";
533
				fireWarningEvent(message, event, 4);
534
				if (isNotBlank(infrParAut) || isNotBlank(infrAut)) {
535
					authorStr = infrAut;
536
					paraut = infrParAut;
537
				}
538
			}
539
			if (StringUtils.isNotBlank(authorStr)) {
540
				INomenclaturalAuthor[] authorAndEx = authorAndEx(authorStr,
541
						event);
542
				name.setCombinationAuthorTeam(authorAndEx[0]);
543
				name.setExCombinationAuthorTeam(authorAndEx[1]);
544
			}
545
			if (StringUtils.isNotBlank(paraut)) {
546
				INomenclaturalAuthor[] authorAndEx = authorAndEx(paraut, event);
547
				name.setBasionymAuthorTeam(authorAndEx[0]);
548
				name.setExBasionymAuthorTeam(authorAndEx[1]);
549
			}
550
		}
551
	}
552

    
553
	private TeamOrPersonBase[] authorAndEx(String authorAndEx, XMLEvent xmlEvent) {
554
		authorAndEx = authorAndEx.trim();
555
		TeamOrPersonBase[] result = new TeamOrPersonBase[2];
556

    
557
		String[] split = authorAndEx.split("\\sex\\s");
558
		if (split.length > 2) {
559
			String message = "There is more then 1 ' ex ' in author string. Can't separate author and ex-author";
560
			fireWarningEvent(message, xmlEvent, 4);
561
			result[0] = createAuthor(authorAndEx);
562
		} else if (split.length == 2) {
563
			result[0] = createAuthor(split[1]);
564
			result[1] = createAuthor(split[0]);
565
		} else {
566
			result[0] = createAuthor(split[0]);
567
		}
568
		return result;
569
	}
570

    
571
	/**
572
	 * Returns the (empty) name with the correct homotypical group depending on
573
	 * the taxon status. Throws NPE if no currentTaxon is set in state.
574
	 * 
575
	 * @param state
576
	 * @param homotypicalGroup
577
	 * @param isSynonym
578
	 * @return
579
	 */
580
	private NonViralName<?> createName(MarkupImportState state,
581
			HomotypicalGroup homotypicalGroup, boolean isSynonym) {
582
		NonViralName<?> name;
583
		Taxon taxon = state.getCurrentTaxon();
584
		if (isSynonym) {
585
			Rank defaultRank = Rank.SPECIES(); // can be any
586
			name = createNameByCode(state, defaultRank);
587
			if (homotypicalGroup != null) {
588
				name.setHomotypicalGroup(homotypicalGroup);
589
			}
590
			SynonymRelationshipType synonymType = SynonymRelationshipType
591
					.HETEROTYPIC_SYNONYM_OF();
592
			if (taxon.getHomotypicGroup().equals(homotypicalGroup)) {
593
				synonymType = SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF();
594
			}
595
			taxon.addSynonymName(name, synonymType);
596
		} else {
597
			name = CdmBase.deproxy(taxon.getName(), NonViralName.class);
598
		}
599
		return name;
600
	}
601

    
602
	private void handleCitation(MarkupImportState state, XMLEventReader reader,
603
			XMLEvent parentEvent, NonViralName name) throws XMLStreamException {
604
		String classValue = getClassOnlyAttribute(parentEvent);
605

    
606
		state.setCitation(true);
607
		boolean hasRefPart = false;
608
		Map<String, String> refMap = new HashMap<String, String>();
609
		while (reader.hasNext()) {
610
			XMLEvent next = readNoWhitespace(reader);
611
			if (isMyEndingElement(next, parentEvent)) {
612
				checkMandatoryElement(hasRefPart, parentEvent.asStartElement(),
613
						REF_PART);
614
				Reference<?> reference = createReference(state, refMap, next);
615
				String microReference = refMap.get(DETAILS);
616
				doCitation(state, name, classValue, reference, microReference,
617
						parentEvent);
618
				state.setCitation(false);
619
				return;
620
			} else if (isStartingElement(next, REF_PART)) {
621
				handleRefPart(state, reader, next, refMap);
622
				hasRefPart = true;
623
			} else {
624
				handleUnexpectedElement(next);
625
			}
626
		}
627
		throw new IllegalStateException("Citation has no closing tag");
628

    
629
	}
630

    
631
	private void handleRefPart(MarkupImportState state, XMLEventReader reader,
632
			XMLEvent parentEvent, Map<String, String> refMap)
633
			throws XMLStreamException {
634
		String classValue = getClassOnlyAttribute(parentEvent);
635

    
636
		String text = "";
637
		while (reader.hasNext()) {
638
			XMLEvent next = readNoWhitespace(reader);
639
			if (isMyEndingElement(next, parentEvent)) {
640
				refMap.put(classValue, text);
641
				return;
642
			} else if (next.isStartElement()) {
643
				if (isStartingElement(next, ANNOTATION)) {
644
					handleNotYetImplementedElement(next); // TODO test
645
															// handleSimpleAnnotation
646
				} else if (isStartingElement(next, ITALICS)) {
647
					handleNotYetImplementedElement(next);
648
				} else if (isStartingElement(next, BOLD)) {
649
					handleNotYetImplementedElement(next);
650
				} else {
651
					handleUnexpectedStartElement(next.asStartElement());
652
				}
653
			} else if (next.isCharacters()) {
654
				text += next.asCharacters().getData();
655
			} else {
656
				handleUnexpectedEndElement(next.asEndElement());
657
			}
658
		}
659
		throw new IllegalStateException("RefPart has no closing tag");
660

    
661
	}
662

    
663
	private void doCitation(MarkupImportState state, NonViralName name,
664
			String classValue, Reference reference, String microCitation,
665
			XMLEvent parentEvent) {
666
		if (PUBLICATION.equalsIgnoreCase(classValue)) {
667
			name.setNomenclaturalReference(reference);
668
			name.setNomenclaturalMicroReference(microCitation);
669
		} else if (USAGE.equalsIgnoreCase(classValue)) {
670
			Taxon taxon = state.getCurrentTaxon();
671
			TaxonDescription td = getTaxonDescription(taxon, state.getConfig()
672
					.getSourceReference(), false, true);
673
			TextData citation = TextData.NewInstance(Feature.CITATION());
674
			// TODO name used in source
675
			citation.addSource(null, null, reference, microCitation);
676
			td.addElement(citation);
677
		} else if (TYPE.equalsIgnoreCase(classValue)) {
678
			handleNotYetImplementedAttributeValue(parentEvent, CLASS,
679
					classValue);
680
		} else {
681
			// TODO Not yet implemented
682
			handleNotYetImplementedAttributeValue(parentEvent, CLASS,
683
					classValue);
684
		}
685
	}
686

    
687
	/**
688
	 * Tests if the names rank is consistent with the given author strings.
689
	 * NOTE: Tags for authors are differ depending on the rank.
690
	 * 
691
	 * @param name
692
	 * @param event
693
	 * @param authorStr
694
	 * @param paraut
695
	 * @param infrParAut
696
	 * @param infrAut
697
	 */
698
	private void testRankAuthorConsistency(NonViralName name, XMLEvent event,
699
			String authorStr, String paraut, String infrParAut, String infrAut) {
700
		if (name.getRank() == null) {
701
			return;
702
		}
703
		if (name.getRank().isInfraSpecific()) {
704
			if (StringUtils.isBlank(infrParAut)
705
					&& StringUtils.isBlank(infrAut) // was isNotBlank before
706
													// 29.5.2012
707
					&& (StringUtils.isNotBlank(paraut) || StringUtils
708
							.isNotBlank(authorStr)) && !name.isAutonym()) {
709
				String message = "Rank is infraspecicific but has only specific or higher author(s)";
710
				fireWarningEvent(message, event, 4);
711
			}
712
		} else {
713
			// is not infraspecific
714
			if (StringUtils.isNotBlank(infrParAut)
715
					|| StringUtils.isNotBlank(infrAut)) {
716
				String message = "Rank is not infraspecicific but name has infra author(s)";
717
				fireWarningEvent(message, event, 4);
718
			}
719
		}
720
	}
721

    
722
	private Reference<?> createReference(MarkupImportState state,
723
			Map<String, String> refMap, XMLEvent parentEvent) {
724
		// TODO
725
		Reference<?> reference;
726

    
727
		String type = getAndRemoveMapKey(refMap, PUBTYPE);
728
		String authorStr = getAndRemoveMapKey(refMap, AUTHOR);
729
		String titleStr = getAndRemoveMapKey(refMap, PUBTITLE);
730
		String titleCache = getAndRemoveMapKey(refMap, PUBFULLNAME);
731
		String volume = getAndRemoveMapKey(refMap, VOLUME);
732
		String edition = getAndRemoveMapKey(refMap, EDITION);
733
		String editors = getAndRemoveMapKey(refMap, EDITORS);
734
		String year = getAndRemoveMapKey(refMap, YEAR);
735
		String pubName = getAndRemoveMapKey(refMap, PUBNAME);
736
		String pages = getAndRemoveMapKey(refMap, PAGES);
737

    
738
		if (state.isCitation()) {
739
			if (volume != null || "journal".equalsIgnoreCase(type)) {
740
				IArticle article = ReferenceFactory.newArticle();
741
				if (pubName != null) {
742
					IJournal journal = ReferenceFactory.newJournal();
743
					journal.setTitle(pubName);
744
					article.setInJournal(journal);
745
				}
746
				reference = (Reference<?>) article;
747

    
748
			} else {
749
				// TODO
750
				if (pubName != null) {
751
					reference = ReferenceFactory.newBookSection();
752
				} else {
753
					reference = ReferenceFactory.newBook();
754
				}
755
			}
756
			// TODO use existing author from name or before
757
			TeamOrPersonBase<?> author = createAuthor(authorStr);
758
			reference.setAuthorTeam(author);
759

    
760
			reference.setTitle(titleStr);
761
			if (StringUtils.isNotBlank(titleCache)) {
762
				reference.setTitleCache(titleCache, true);
763
			}
764
			reference.setEdition(edition);
765
			reference.setEditor(editors);
766

    
767
			if (pubName != null) {
768
				Reference<?> inReference;
769
				if (reference.getType().equals(ReferenceType.Article)) {
770
					inReference = ReferenceFactory.newJournal();
771
				} else {
772
					inReference = ReferenceFactory.newGeneric();
773
				}
774
				inReference.setTitle(pubName);
775
				reference.setInReference(inReference);
776
			}
777

    
778
		} else { // no citation
779
			if (volume != null || "journal".equalsIgnoreCase(type)) {
780
				IArticle article = ReferenceFactory.newArticle();
781
				if (pubName != null) {
782
					IJournal journal = ReferenceFactory.newJournal();
783
					journal.setTitle(pubName);
784
					article.setInJournal(journal);
785
				}
786
				reference = (Reference<?>) article;
787

    
788
			} else {
789
				Reference<?> bookOrPartOf = ReferenceFactory.newGeneric();
790
				reference = bookOrPartOf;
791
			}
792

    
793
			// TODO type
794
			TeamOrPersonBase<?> author = createAuthor(authorStr);
795
			reference.setAuthorTeam(author);
796

    
797
			reference.setTitle(titleStr);
798
			if (StringUtils.isNotBlank(titleCache)) {
799
				reference.setTitleCache(titleCache, true);
800
			}
801
			reference.setEdition(edition);
802
			reference.setEditor(editors);
803

    
804
			if (pubName != null) {
805
				Reference<?> inReference;
806
				if (reference.getType().equals(ReferenceType.Article)) {
807
					inReference = ReferenceFactory.newJournal();
808
				} else {
809
					inReference = ReferenceFactory.newGeneric();
810
				}
811
				inReference.setTitle(pubName);
812
				reference.setInReference(inReference);
813
			}
814
		}
815
		reference.setVolume(volume);
816
		reference.setDatePublished(TimePeriod.parseString(year));
817
		// TODO check if this is handled correctly in FM markup
818
		reference.setPages(pages);
819

    
820
		// TODO
821
		String[] unhandledList = new String[] { ALTERNATEPUBTITLE, ISSUE,
822
				NOTES, STATUS };
823
		for (String unhandled : unhandledList) {
824
			String value = getAndRemoveMapKey(refMap, unhandled);
825
			if (isNotBlank(value)) {
826
				this.handleNotYetImplementedAttributeValue(parentEvent, CLASS,
827
						unhandled);
828
			}
829
		}
830

    
831
		for (String key : refMap.keySet()) {
832
			if (!DETAILS.equalsIgnoreCase(key)) {
833
				this.fireUnexpectedAttributeValue(parentEvent, CLASS, key);
834
			}
835
		}
836

    
837
		return reference;
838
	}
839

    
840
	public Reference<?> handleReference(MarkupImportState state,
841
			XMLEventReader reader, XMLEvent parentEvent)
842
			throws XMLStreamException {
843
		checkNoAttributes(parentEvent);
844

    
845
		boolean hasRefPart = false;
846
		Map<String, String> refMap = new HashMap<String, String>();
847
		while (reader.hasNext()) {
848
			XMLEvent next = readNoWhitespace(reader);
849
			if (isMyEndingElement(next, parentEvent)) {
850
				checkMandatoryElement(hasRefPart, parentEvent.asStartElement(),
851
						REF_PART);
852
				Reference<?> reference = createReference(state, refMap, next);
853
				return reference;
854
			} else if (isStartingElement(next, REF_PART)) {
855
				handleRefPart(state, reader, next, refMap);
856
				hasRefPart = true;
857
			} else {
858
				handleUnexpectedElement(next);
859
			}
860
		}
861
		// TODO handle missing end element
862
		throw new IllegalStateException("<Reference> has no closing tag");
863
	}
864

    
865
}
(13-13/17)