Project

General

Profile

« Previous | Next » 

Revision 484431ef

Added by Katja Luther over 12 years ago

trunk merged into branch

View differences:

cdmlib-io/src/main/java/eu/etaxonomy/cdm/io/markup/MarkupDocumentImport.java
1 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
*/ 
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 9

  
10 10
package eu.etaxonomy.cdm.io.markup;
11 11

  
12
import java.net.MalformedURLException;
13
import java.net.URL;
12 14
import java.util.ArrayList;
15
import java.util.Arrays;
13 16
import java.util.HashMap;
14 17
import java.util.HashSet;
15 18
import java.util.LinkedList;
......
22 25
import java.util.regex.Pattern;
23 26

  
24 27
import javax.xml.stream.FactoryConfigurationError;
28
import javax.xml.stream.Location;
25 29
import javax.xml.stream.XMLEventReader;
26 30
import javax.xml.stream.XMLStreamException;
31
import javax.xml.stream.events.Attribute;
32
import javax.xml.stream.events.StartElement;
27 33
import javax.xml.stream.events.XMLEvent;
28 34

  
29
import org.apache.commons.lang.CharUtils;
30 35
import org.apache.commons.lang.StringUtils;
31 36
import org.apache.log4j.Logger;
32
import org.jdom.Attribute;
33
import org.jdom.Element;
37
import org.springframework.beans.factory.annotation.Autowired;
38
import org.springframework.security.access.PermissionEvaluator;
39
import org.springframework.security.authentication.AuthenticationManager;
40
import org.springframework.security.core.Authentication;
34 41
import org.springframework.stereotype.Component;
42
import org.springframework.transaction.TransactionStatus;
35 43

  
44
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
45
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade.DerivedUnitType;
46
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeCacheStrategy;
36 47
import eu.etaxonomy.cdm.common.CdmUtils;
37
import eu.etaxonomy.cdm.common.ResultWrapper;
38
import eu.etaxonomy.cdm.common.XmlHelp;
48
import eu.etaxonomy.cdm.ext.geo.GeoServiceArea;
49
import eu.etaxonomy.cdm.ext.geo.IEditGeoService;
39 50
import eu.etaxonomy.cdm.io.common.ICdmIO;
40 51
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
41 52
import eu.etaxonomy.cdm.io.markup.UnmatchedLeads.UnmatchedLeadsKey;
42
import eu.etaxonomy.cdm.model.agent.Person;
53
import eu.etaxonomy.cdm.model.agent.AgentBase;
54
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
43 55
import eu.etaxonomy.cdm.model.agent.Team;
44 56
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
45 57
import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
46 58
import eu.etaxonomy.cdm.model.common.Annotation;
47 59
import eu.etaxonomy.cdm.model.common.AnnotationType;
48 60
import eu.etaxonomy.cdm.model.common.CdmBase;
49
import eu.etaxonomy.cdm.model.common.Credit;
61
import eu.etaxonomy.cdm.model.common.Extension;
50 62
import eu.etaxonomy.cdm.model.common.ExtensionType;
51
import eu.etaxonomy.cdm.model.common.ISourceable;
52 63
import eu.etaxonomy.cdm.model.common.Language;
53
import eu.etaxonomy.cdm.model.common.Marker;
54
import eu.etaxonomy.cdm.model.common.MarkerType;
64
import eu.etaxonomy.cdm.model.common.TermVocabulary;
55 65
import eu.etaxonomy.cdm.model.common.TimePeriod;
56
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
57 66
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
67
import eu.etaxonomy.cdm.model.description.Distribution;
58 68
import eu.etaxonomy.cdm.model.description.Feature;
59 69
import eu.etaxonomy.cdm.model.description.KeyStatement;
60 70
import eu.etaxonomy.cdm.model.description.PolytomousKey;
61 71
import eu.etaxonomy.cdm.model.description.PolytomousKeyNode;
72
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;
73
import eu.etaxonomy.cdm.model.description.PresenceTerm;
62 74
import eu.etaxonomy.cdm.model.description.TaxonDescription;
63 75
import eu.etaxonomy.cdm.model.description.TextData;
64
import eu.etaxonomy.cdm.model.name.BotanicalName;
76
import eu.etaxonomy.cdm.model.location.NamedArea;
77
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
78
import eu.etaxonomy.cdm.model.location.NamedAreaType;
79
import eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity;
80
import eu.etaxonomy.cdm.model.media.Media;
81
import eu.etaxonomy.cdm.model.name.CultivarPlantName;
65 82
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
66
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
67
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
83
import eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus;
68 84
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
85
import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
86
import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
69 87
import eu.etaxonomy.cdm.model.name.NonViralName;
70 88
import eu.etaxonomy.cdm.model.name.Rank;
71
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
72 89
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
73 90
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
74
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
91
import eu.etaxonomy.cdm.model.occurrence.Collection;
92
import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
93
import eu.etaxonomy.cdm.model.occurrence.FieldObservation;
75 94
import eu.etaxonomy.cdm.model.occurrence.Specimen;
76
import eu.etaxonomy.cdm.model.reference.IBook;
95
import eu.etaxonomy.cdm.model.reference.IArticle;
77 96
import eu.etaxonomy.cdm.model.reference.IJournal;
78 97
import eu.etaxonomy.cdm.model.reference.Reference;
79 98
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
......
81 100
import eu.etaxonomy.cdm.model.taxon.Classification;
82 101
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
83 102
import eu.etaxonomy.cdm.model.taxon.Taxon;
84
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
85 103
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
86 104
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
105
import eu.etaxonomy.cdm.strategy.parser.NameTypeParser;
87 106
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
88

  
107
import eu.etaxonomy.cdm.strategy.parser.SpecimenTypeParser;
108
import eu.etaxonomy.cdm.strategy.parser.SpecimenTypeParser.TypeInfo;
89 109

  
90 110
/**
91 111
 * @author a.mueller
92
 *
112
 * 
93 113
 */
94 114
@Component
95 115
public class MarkupDocumentImport  extends MarkupImportBase implements ICdmIO<MarkupImportState> {
96 116
	private static final Logger logger = Logger.getLogger(MarkupDocumentImport.class);
97 117

  
98
	private static int modCount = 30000;
118
	private static final boolean CREATE_NEW = true;
119
	private static final boolean IS_IMAGE_GALLERY = true;
120
	private static final boolean NO_IMAGE_GALLERY = false;
121

  
122

  
123
	private static final String ACCEPTED = "accepted";
124
	private static final String ACCEPTED_NAME = "acceptedName";
125
	private static final String ADDENDA = "addenda";
126
	private static final String ALTERNATEPUBTITLE = "alternatepubtitle";
127
	private static final String ALTERNATIVE_COLLECTION_TYPE_STATUS = "alternativeCollectionTypeStatus";
128
	private static final String ALTERNATIVE_COLLECTOR = "alternativeCollector";
129
	private static final String ALTERNATIVE_FIELD_NUM = "alternativeFieldNum";
130
	private static final String ALTITUDE = "altitude";
131
	private static final String ANNOTATION = "annotation";
132
	private static final String AUTHOR = "author";
133
	private static final String BIBLIOGRAPHY = "bibliography";
134
	private static final String BIOGRAPHIES = "biographies";
135
	private static final String BOLD = "bold";
136
	private static final String BR = "br";
137
	private static final String CHAR = "char";
138
	private static final String CITATION = "citation";
139
	private static final String COLLECTION_TYPE_STATUS = "collectionTypeStatus";
140
	private static final String COLLECTOR = "collector";
141
	private static final String COORDINATES = "coordinates";
142
	private static final String COUPLET = "couplet";
143
	private static final String DATES = "dates";
144
	private static final String DEDICATION = "dedication";
145
	private static final String DEFAULT_MEDIA_URL = "defaultMediaUrl";
146
	private static final String DESTROYED = "destroyed";
147
	private static final String DETAILS = "details";
148
	private static final String DISTRIBUTION_LIST = "distributionList";
149
	private static final String DISTRIBUTION_LOCALITY = "distributionLocality";
150
	private static final String EDITION = "edition";
151
	private static final String EDITORS = "editors";
152
	private static final String FEATURE = "feature";
153
	private static final String FIGURE = "figure";
154
	private static final String FIGURE_LEGEND = "figureLegend";
155
	private static final String FIGURE_PART = "figurePart";
156
	private static final String FIGURE_REF = "figureRef";
157
	private static final String FIGURE_TITLE = "figureTitle";
158
	private static final String FOOTNOTE = "footnote";
159
	private static final String FOOTNOTE_REF = "footnoteRef";
160
	private static final String FOOTNOTE_STRING = "footnoteString";
161
	private static final String FIELD_NUM = "fieldNum";
162
	private static final String FREQUENCY = "frequency";
163
	private static final String FULL_NAME = "fullName";
164
	private static final String FULL_TYPE = "fullType";
165
	private static final String GATHERING = "gathering";
166
	private static final String HEADING = "heading";
167
	private static final String HABITAT = "habitat";
168
	private static final String HABITAT_LIST = "habitatList";
169
	private static final String HOMONYM = "homonym";
170
	private static final String HOMOTYPES = "homotypes";
171
	private static final String ID = "id";
172
	private static final String INFRANK = "infrank";
173
	private static final String INFRAUT = "infraut";
174
	private static final String INFRPARAUT = "infrparaut";
175
	private static final String IS_SPOTCHARACTERS = "isSpotcharacters";
176
	private static final String ISSUE = "issue";
177
	private static final String ITALICS = "italics";
178
	private static final String KEY = "key";
179
	private static final String KEY_TITLE = "keyTitle";
180
	private static final String KEYNOTES = "keynotes";
181
	private static final String LIFE_CYCLE_PERIODS = "lifeCyclePeriods";
182
	private static final String LOCALITY = "locality";
183
	private static final String LOST = "lost";
184
	private static final String META_DATA = "metaData";
185
	private static final String NAME = "name";
186
	private static final String NAME_TYPE = "nameType";
187
	private static final String NOM = "nom";
188
	private static final String NOMENCLATURE = "nomenclature";
189
	private static final String NOT_FOUND = "notFound";
190
	private static final String NOT_SEEN = "notSeen";
191
	private static final String NOTES = "notes";
192
	private static final String NUM = "num";
193
	private static final String ORIGINAL_DETERMINATION = "originalDetermination";
194
	private static final String PARAUT = "paraut";
195
	private static final String PUBFULLNAME = "pubfullname";
196
	private static final String PUBLICATION = "publication";
197
	private static final String PUBNAME = "pubname";
198
	private static final String PUBTITLE = "pubtitle";
199
	private static final String PUBTYPE = "pubtype";
200
	private static final String QUESTION = "question";
201
	private static final String QUOTE = "quote";
202
	private static final String RANK = "rank";
203
	private static final String REF = "ref";
204
	private static final String REF_NUM = "refNum";
205
	private static final String REF_PART = "refPart";
206
	private static final String REFERENCE = "reference";
207
	private static final String REFERENCES = "references";
208
	private static final String TAXON = "taxon";
209
	private static final String TAXONTITLE = "taxontitle";
210
	private static final String TAXONTYPE = "taxontype";
211
	private static final String TEXT = "text";
212
	private static final String TEXT_SECTION = "textSection";
213
	private static final String TO_COUPLET = "toCouplet";
214
	private static final String TO_KEY = "toKey";
215
	private static final String TO_TAXON = "toTaxon";
216
	private static final String TYPE = "type";
217
	private static final String TYPE_STATUS = "typeStatus";
218
	private static final String TREATMENT = "treatment";
219
	private static final String SERIALS_ABBREVIATIONS = "serialsAbbreviations";
220
	private static final String SPECIMEN_TYPE = "specimenType";
221
	private static final String STATUS = "status";
222
	private static final String STRING = "string";
223
	private static final String SUB_HEADING = "subHeading";
224
	private static final String SUB_COLLECTION = "subCollection";
225
	private static final String SYNONYM = "synonym";
226
	private static final String UNKNOWN = "unknown";
227
	private static final String URL = "url";
228
	private static final String USAGE = "usage";
229
	private static final String VOLUME = "volume";
230
	private static final String WRITER = "writer";
231
	private static final String YEAR = "year";
232

  
99 233
	private NonViralNameParserImpl parser = new NonViralNameParserImpl();
100
	
101
	//TODO make part of state, but state is renewed when invoking the import a second time 
234

  
235
	// TODO make part of state, but state is renewed when invoking the import a
236
	// second time
102 237
	private UnmatchedLeads unmatchedLeads;
103 238

  
239
	@Autowired
240
	private IEditGeoService editGeoService;
104 241
	
105
	public MarkupDocumentImport(){
242
	// TODO remove preliminary
243
	@Autowired
244
	private AuthenticationManager authenticationManager;
245
	private Authentication authentication;
246
	private PermissionEvaluator permissionEvaluator;
247

  
248
	public MarkupDocumentImport() {
106 249
		super();
250
		System.out.println("TODO remove preliminary authentication");
251
		// UsernamePasswordAuthenticationToken token = new
252
		// UsernamePasswordAuthenticationToken("admin", "0000");
253
		// authentication = authenticationManager.authenticate(token);
254
		// SecurityContext context = SecurityContextHolder.getContext();
255
		// context.setAuthentication(authentication);
256
		// permissionEvaluator = new CdmPermissionEvaluator();
107 257
	}
108
	
109
	
258

  
110 259
	@Override
111
	public boolean doCheck(MarkupImportState state){
260
	public boolean doCheck(MarkupImportState state) {
112 261
		state.setCheck(true);
113
		boolean result = doInvoke(state);
262
		doInvoke(state);
114 263
		state.setCheck(false);
115
		return result;
264
		return state.isSuccess();
116 265
	}
117
	
266

  
118 267
	@Override
119
	public boolean doInvoke(MarkupImportState state){
268
	public void doInvoke(MarkupImportState state) { 
120 269
		fireProgressEvent("Start import markup document", "Before start of document");
121
		fireWarningEvent("Test a warning", "At start", 17);
122
		boolean success = false;
123 270
		
124 271
		Queue<CdmBase> outputStream = new LinkedList<CdmBase>();
125
		
126
		//FIXME reset state
272

  
273
		TransactionStatus tx = startTransaction();
274
		// FIXME reset state
127 275
		doAllTheOldOtherStuff(state);
128 276

  
129
		//START
277
		// START
130 278
		try {
131
			//StAX
132
			XMLEventReader reader = getStaxReader(state); 
279
			// StAX
280
			XMLEventReader reader = getStaxReader(state);
133 281
			state.setReader(reader);
134
			//start document
135
			if (! validateStartOfDocument(reader)){
136
				return false;
282
			// start document
283
			if (!validateStartOfDocument(reader)) {
284
				state.setUnsuccessfull();
285
				return;
137 286
			}
287

  
288
			// publication
289
			String elName = PUBLICATION;
290
			boolean hasPublication = false;
138 291
			
139
			//publication
140
			String elName = "publication";
141 292
			while (reader.hasNext()) {
142
				if (isStartingElement(reader, elName)){
143
					success &= handlePublication(reader, elName);
144
				}else{
293
				XMLEvent nextEvent = reader.nextEvent();
294
				if (isStartingElement(nextEvent, elName)) {
295
					handlePublication(state, reader, nextEvent, elName);
296
					hasPublication = true;
297
				} else if (nextEvent.isEndDocument()) {
298
					if (!hasPublication) {
299
						String message = "No publication root element found";
300
						fireWarningEvent(message, nextEvent, 8);
301
					}
302
					// done
303
				} else {
145 304
					fireSchemaConflictEventExpectedStartTag(elName, reader);
146 305
				}
147 306
			}
148
			
149
//			//SAX
150
//			ImportHandlerBase handler= new PublicationHandler(this);
151
//			parseSAX(state, handler);
152
			
307
			commitTransaction(tx);
308

  
309
			// //SAX
310
			// ImportHandlerBase handler= new PublicationHandler(this);
311
			// parseSAX(state, handler);
312

  
153 313
		} catch (FactoryConfigurationError e1) {
154
			// TODO Auto-generated catch block
155 314
			fireWarningEvent("Some error occurred while setting up xml factory. Data can't be imported", "Start", 16);
156
			success = false;
315
			state.setUnsuccessfull();
157 316
		} catch (XMLStreamException e1) {
158
			// TODO Auto-generated catch block
159 317
			fireWarningEvent("An XMLStreamException occurred while parsing. Data can't be imported", "Start", 16);
160
			success = false;
161
//		} catch (ParserConfigurationException e) {
162
//			fireWarningEvent("A ParserConfigurationException occurred while parsing. Data can't be imported", "Start", 16);
163
//		} catch (SAXException e) {
164
//			fireWarningEvent("A SAXException occurred while parsing. Data can't be imported", "Start", 16);
165
//		} catch (IOException e) {
166
//			fireWarningEvent("An IO exception occurred while parsing. Data can't be imported", "Start", 16);
318
			state.setUnsuccessfull();
319
			// } catch (ParserConfigurationException e) {
320
			// fireWarningEvent("A ParserConfigurationException occurred while parsing. Data can't be imported",
321
			// "Start", 16);
322
			// } catch (SAXException e) {
323
			// fireWarningEvent("A SAXException occurred while parsing. Data can't be imported",
324
			// "Start", 16);
325
			// } catch (IOException e) {
326
			// fireWarningEvent("An IO exception occurred while parsing. Data can't be imported",
327
			// "Start", 16);
167 328

  
168 329
		}
169 330
		 
170
	
171
		return success;
172
		
173
	}
174

  
175

  
176
	private boolean handlePublication(XMLEventReader reader, String elName) throws XMLStreamException {
177
		boolean success = true;
178
		XMLEvent event = reader.nextEvent();
179
		if (isEndingElement(event, elName)){
180
			//TODO cardinality of alternative
181
			return success;
182
		}else if(isStartingElement(event, "metaData")){
183
			
184
		}else if(isStartingElement(event, "treatment")){
185
			
186
		}else if(isStartingElement(event, "biographies")){
187
			
188
		}else if(isStartingElement(event, "references")){
189
			
190
		}else if(isStartingElement(event, "metaData")){
191
			
192
		}else if(isStartingElement(event, "metaData")){
193
			
194
		}else if(isStartingElement(event, "metaData")){
195
			
196
		}
197
		return success;
198
	}
199
	
331
		return;
200 332

  
201
	
202
	/**
203
	 * This comes from the old version, needs to be checked on need
204
	 * @param state
205
	 */
206
	private void doAllTheOldOtherStuff(MarkupImportState state) {
207
		state.putTree(null, null);
208
		if (unmatchedLeads == null){
209
			unmatchedLeads = UnmatchedLeads.NewInstance();
210
		}
211
		state.setUnmatchedLeads(unmatchedLeads);
212
		
213
//		TransactionStatus tx = startTransaction();
214
		unmatchedLeads.saveToSession(getPolytomousKeyNodeService());
215
		
216
		
217
		//TODO generally do not store the reference object in the config
218
		Reference sourceReference = state.getConfig().getSourceReference();
219
		getReferenceService().saveOrUpdate(sourceReference);
220 333
	}
221 334

  
335
	private void handlePublication(MarkupImportState state,
336
			XMLEventReader reader, XMLEvent currentEvent, String elName)
337
			throws XMLStreamException {
222 338

  
223
	private boolean doInvoke_old(MarkupImportState state){
224
		Set<TaxonBase> taxaToSave = new HashSet<TaxonBase>();
225
		ResultWrapper<Boolean> success = ResultWrapper.NewInstance(true);
226

  
227
	//	Element elbody= getBodyElement(state.getConfig());
228
		Element elbody = null;
229
		List<Element> elTaxonList = elbody.getChildren();
230
		
231
		int i = 0;
232
		
233
		Set<String> unhandledTitleClassess = new HashSet<String>();
234
		Set<String> unhandledNomeclatureChildren = new HashSet<String>();
235
		Set<String> unhandledDescriptionChildren = new HashSet<String>();
236
		
237
		Taxon lastTaxon = getLastTaxon(state);
238
		
239
		//for each taxon
240
		for (Element elTaxon : elTaxonList){
339
		// attributes
340
		StartElement element = currentEvent.asStartElement();
341
		Map<String, Attribute> attributes = getAttributes(element);
342
		handleUnexpectedAttributes(element.getLocation(), attributes,
343
				"noNamespaceSchemaLocation");
344

  
345
		while (reader.hasNext()) {
346
			XMLEvent event = readNoWhitespace(reader);
347
			// TODO cardinality of alternative
348
			if (event.isEndElement()) {
349
				if (isEndingElement(event, elName)) {
350
					return;
351
				} else {
352
					if (isEndingElement(event, BIOGRAPHIES)) {
353
						// NOT YET IMPLEMENTED
354
						popUnimplemented(event.asEndElement());
355
					} else if (isEndingElement(event, REFERENCES)) {
356
						// NOT YET IMPLEMENTED
357
						popUnimplemented(event.asEndElement());
358
					} else if (isEndingElement(event, TEXT_SECTION)) {
359
						// NOT YET IMPLEMENTED
360
						popUnimplemented(event.asEndElement());
361
					} else if (isEndingElement(event, ADDENDA)) {
362
						// NOT YET IMPLEMENTED
363
						popUnimplemented(event.asEndElement());
364
					} else {
365
						handleUnexpectedElement(event);
366
		}
367
	}
368
			} else if (event.isStartElement()) {
369
				if (isStartingElement(event, META_DATA)) {
370
					handleMetaData(state, reader, event);
371
				} else if (isStartingElement(event, TREATMENT)) {
372
					handleTreatment(state, reader, event);
373
				} else if (isStartingElement(event, BIOGRAPHIES)) {
374
					handleNotYetImplementedElement(event);
375
				} else if (isStartingElement(event, REFERENCES)) {
376
					handleNotYetImplementedElement(event);
377
				} else if (isStartingElement(event, TEXT_SECTION)) {
378
					handleNotYetImplementedElement(event);
379
				} else if (isStartingElement(event, ADDENDA)) {
380
					handleNotYetImplementedElement(event);
381
				} else {
382
					handleUnexpectedStartElement(event);
383
		}
384
			} else {
385
				handleUnexpectedElement(event);
386
	}
387
		}
388
		throw new IllegalStateException("Publication has no ending element");
389
	}
390

  
391
	private void handleMetaData(MarkupImportState state, XMLEventReader reader,
392
			XMLEvent parentEvent) throws XMLStreamException {
393
		checkNoAttributes(parentEvent);
394

  
395
		while (reader.hasNext()) {
396
			XMLEvent next = readNoWhitespace(reader);
397
			if (isMyEndingElement(next, parentEvent)) {
398
				return;
399
			} else if (isStartingElement(next, DEFAULT_MEDIA_URL)) {
400
				String baseUrl = getCData(state, reader, next);
241 401
			try {
242
				if ((i++ % modCount) == 0 && i > 1){ logger.info("Taxa handled: " + (i-1));}
243
				if (! elTaxon.getName().equalsIgnoreCase("taxon")){
244
					logger.warn("body has element other than 'taxon'");
402
					new URL(baseUrl);
403
					state.setBaseMediaUrl(baseUrl);
404
				} catch (MalformedURLException e) {
405
					String message = "defaultMediaUrl '%s' is not a valid URL";
406
					message = String.format(message, baseUrl);
407
					fireWarningEvent(message, next, 8);
245 408
				}
246
				
247
				BotanicalName botanicalName = BotanicalName.NewInstance(Rank.SPECIES());
248
				Taxon taxon = Taxon.NewInstance(botanicalName, state.getConfig().getSourceReference());
249
				
250
				handleTaxonAttributes(elTaxon, taxon, state);
251
	
252
				
253
				List<Element> children = elTaxon.getChildren();
254
				handleTaxonElement(state, unhandledTitleClassess, unhandledNomeclatureChildren,	unhandledDescriptionChildren, taxon, children);
255
				handleTaxonRelation(state, taxon, lastTaxon);
256
				lastTaxon = taxon;
257
				taxaToSave.add(taxon);
258
				state.getConfig().setLastTaxonUuid(lastTaxon.getUuid());
259
				
260
			} catch (Exception e) {
261
				logger.warn("Exception occurred in Sapindacea taxon import: " + e);
262
				e.printStackTrace();
409
			} else {
410
				handleUnexpectedElement(next);
263 411
			}
264
			
265 412
		}
266
		
267
		System.out.println(state.getUnmatchedLeads().toString());
268
		logger.warn("There are taxa with attributes 'excluded' and 'dubious'");
269
		
270
		logger.info("Children for nomenclature are: " + unhandledNomeclatureChildren);
271
		logger.info("Children for description are: " + unhandledDescriptionChildren);
272
		logger.info("Children for homotypes are: " + unhandledHomotypeChildren);
273
		logger.info("Children for nom are: " + unhandledNomChildren);
274
		
275
		
276
		//invokeRelations(source, cdmApp, deleteAll, taxonMap, referenceMap);
277
		logger.info(i + " taxa handled. Saving ...");
278
		getTaxonService().saveOrUpdate(taxaToSave);
279
		getFeatureTreeService().saveOrUpdateFeatureNodesAll(state.getFeatureNodesToSave());
280
		state.getFeatureNodesToSave().clear();
281
//		commitTransaction(tx);
282
		
283
		logger.info("end makeTaxa ...");
284
		logger.info("start makeKey ...");
285
	//	invokeDoKey(state);
286
		logger.info("end makeKey ...");
287
		
288
		return success.getValue();
289
	}
290

  
291
	
413
		throw new IllegalStateException("MetaData has no ending element");
292 414

  
415
	}
293 416

  
294
	private void handleTaxonAttributes(Element elTaxon, Taxon taxon, MarkupImportState state) {
295
		List<Attribute> attrList = elTaxon.getAttributes();
296
		for (Attribute attr : attrList){
297
			String attrName = attr.getName();
298
			String attrValue = attr.getValue();
299
			if ("class".equals(attrName)){
300
				if (attrValue.equalsIgnoreCase("dubious") || attrValue.equalsIgnoreCase("DUBIOUS GENUS") || attrValue.equalsIgnoreCase("DOUBTFUL SPECIES")  ){
301
					taxon.setDoubtful(true);
302
				}else{
303
					MarkerType markerType = getMarkerType(state, attrValue);
304
					if (markerType == null){
305
						logger.warn("Class attribute value for taxon not yet supported: " + attrValue);
306
					}else{
307
						taxon.addMarker(Marker.NewInstance(markerType, true));
417
	private void handleTreatment(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
418
		checkNoAttributes(parentEvent);
419
		Taxon lastTaxon = null;
420
		while (reader.hasNext()) {
421
			XMLEvent next = readNoWhitespace(reader);
422
			if (isStartingElement(next, TAXON)) {
423
				Taxon thisTaxon = handleTaxon(state, reader, next.asStartElement());
424
				doTaxonRelation(state, thisTaxon, lastTaxon, parentEvent.getLocation());
425
				lastTaxon = thisTaxon;
426
				// TODO for imports spanning multiple documents ?? Still needed?
427
				state.getConfig().setLastTaxonUuid(lastTaxon.getUuid());
428
			} else if (isMyEndingElement(next, parentEvent)) {
429
				Set<PolytomousKeyNode> keyNodesToSave = state.getPolytomousKeyNodesToSave();
430
				//better save the key then the nodes
431
				Set<PolytomousKey> keySet = new HashSet<PolytomousKey>();
432
				for (PolytomousKeyNode node : keyNodesToSave){
433
					PolytomousKey key = node.getKey();
434
					keySet.add(key);
308 435
					}
436
				save(keySet, state);
437
				//unmatched key leads
438
				UnmatchedLeads unmatched = state.getUnmatchedLeads();
439
				if (unmatched.size() > 0){
440
					String message = "The following key leads are unmatched: %s";
441
					message = String.format(message, state.getUnmatchedLeads().toString());
442
					fireWarningEvent(message, next, 6);
309 443
				}
310
			}else if ("num".equals(attrName)){
311
				logger.warn("num not yet supported");
312
			}else{
313
				logger.warn("Attribute " + attrName + " not yet supported for element taxon");
444
//				save(keyNodesToSave, state);
445

  
446
				return;
447
			} else {
448
				handleUnexpectedElement(next);
314 449
			}
315 450
		}
316

  
451
		return;
317 452
	}
318 453

  
454
	/**
455
	 * @param taxon
456
	 * @param lastTaxon
457
	 */
458
	private void doTaxonRelation(MarkupImportState state, Taxon taxon,
459
			Taxon lastTaxon, Location dataLocation) {
319 460

  
320
	private Taxon getLastTaxon(MarkupImportState state) {
321
		if (state.getConfig().getLastTaxonUuid() == null){
322
			return null;
323
		}else{
324
			return (Taxon)getTaxonService().find(state.getConfig().getLastTaxonUuid());
325
		}
326
	}
327

  
328

  
329
//	private void invokeDoKey(SapindaceaeImportState state) {
330
//		TransactionStatus tx = startTransaction();
331
//		
332
//		Set<FeatureNode> nodesToSave = new HashSet<FeatureNode>();
333
//		ITaxonService taxonService = getTaxonService();
334
//		ResultWrapper<Boolean> success = ResultWrapper.NewInstance(true);
335
//
336
//		Element elbody= getBodyElement(state.getConfig());
337
//		List<Element> elTaxonList = elbody.getChildren();
338
//		
339
//		int i = 0;
340
//		
341
//		//for each taxon
342
//		for (Element elTaxon : elTaxonList){
343
//			if ((i++ % modCount) == 0 && i > 1){ logger.info("Taxa handled: " + (i-1));}
344
//			if (! elTaxon.getName().equalsIgnoreCase("taxon")){
345
//				continue;
346
//			}
347
//			
348
//			List<Element> children = elTaxon.getChildren("key");
349
//			for (Element element : children){
350
//				handleKeys(state, element, null);
351
//			}
352
//			nodesToSave.add(taxon);
353
//
354
//		}
355
//		
356
//	}
461
		Classification tree = makeTree(state, dataLocation);
462
		if (lastTaxon == null) {
463
			tree.addChildTaxon(taxon, null, null, null);
464
			return;
465
		}
466
		Rank thisRank = taxon.getName().getRank();
467
		Rank lastRank = lastTaxon.getName().getRank();
468
		if (lastTaxon.getTaxonNodes().size() > 0) {
469
			TaxonNode lastNode = lastTaxon.getTaxonNodes().iterator().next();
470
			if (thisRank.isLower(lastRank)) {
471
				lastNode.addChildTaxon(taxon, null, null, null);
472
				fillMissingEpithetsForTaxa(lastTaxon, taxon);
473
			} else if (thisRank.equals(lastRank)) {
474
				TaxonNode parent = lastNode.getParent();
475
				if (parent != null) {
476
					parent.addChildTaxon(taxon, null, null, null);
477
					fillMissingEpithetsForTaxa(parent.getTaxon(), taxon);
478
				} else {
479
					tree.addChildTaxon(taxon, null, null, null);
480
	}
481
			} else if (thisRank.isHigher(lastRank)) {
482
				doTaxonRelation(state, taxon, lastNode.getParent().getTaxon(),
483
						dataLocation);
484
				// TaxonNode parentNode = handleTaxonRelation(state, taxon,
485
				// lastNode.getParent().getTaxon());
486
				// parentNode.addChildTaxon(taxon, null, null, null);
487
			}
488
		} else {
357 489

  
490
			String message = "Last taxon has no node";
491
			fireWarningEvent(message, makeLocationStr(dataLocation), 6);
492
		}
493
	}
358 494

  
359
	// body/taxon/*
360
	private void handleTaxonElement(MarkupImportState state, Set<String> unhandledTitleClassess, Set<String> unhandledNomeclatureChildren, Set<String> unhandledDescriptionChildren, Taxon taxon, List<Element> children) {
361
		AnnotatableEntity lastEntity = null;
362
		for (Element element : children){
363
			String elName = element.getName();
364
			
365
			if (elName.equalsIgnoreCase("title")){
366
				handleTitle(state, element, taxon, unhandledTitleClassess);
367
				lastEntity = null;
368
			}else if(elName.equalsIgnoreCase("nomenclature")){
369
				handleNomenclature(state, element, taxon, unhandledNomeclatureChildren);
370
				lastEntity = null;
371
			}else if(elName.equalsIgnoreCase("description")){
372
				handleDescription(state, element, taxon, unhandledDescriptionChildren);
373
				lastEntity = null;
374
			}else if(elName.equalsIgnoreCase("habitatecology")){
375
				lastEntity = handleEcology(state, element, taxon);
376
			}else if(elName.equalsIgnoreCase("distribution")){
377
				lastEntity = handleDistribution(state, element, taxon);
378
			}else if(elName.equalsIgnoreCase("uses")){
379
				lastEntity = handleUses(state, element, taxon);
380
			}else if(elName.equalsIgnoreCase("notes")){
381
				lastEntity = handleTaxonNotes(state, element, taxon);
382
			}else if(elName.equalsIgnoreCase("chromosomes")){
383
				lastEntity = handleChromosomes(state, element, taxon);
384
			}else if(elName.equalsIgnoreCase("vernacularnames")){
385
				handleVernaculars(state, element, taxon);
386
			}else if(elName.equalsIgnoreCase("key")){
387
				lastEntity = handleKeys(state, element, taxon);
388
			}else if(elName.equalsIgnoreCase("references")){
389
				handleReferences(state, element, taxon, lastEntity);
390
				lastEntity = null;
391
			}else if(elName.equalsIgnoreCase("taxon")){
392
				logger.warn("A taxon should not be part of a taxon");
393
			}else if(elName.equalsIgnoreCase("homotypes")){
394
				logger.warn("Homotypes should be included in the nomenclature flag but is child of taxon [XPath: body/taxon/homotypes]");
395
			}else{
396
				logger.warn("Unexpected child for taxon: " + elName);
495
	/**
496
	 * @param state
497
	 * @param dataLocation 
498
	 * @return
499
	 */
500
	private Classification makeTree(MarkupImportState state, Location dataLocation) {
501
		Classification result = state.getTree(null);
502
		if (result == null) {
503
			UUID uuid = state.getConfig().getClassificationUuid();
504
			if (uuid == null) {
505
				String message = "No classification uuid is defined";
506
				fireWarningEvent(message, makeLocationStr(dataLocation), 6);
507
				result = createNewClassification(state);
508
			} else {
509
				result = getClassificationService().find(uuid);
510
				if (result == null) {
511
					result = createNewClassification(state);
512
					result.setUuid(uuid);
397 513
			}
398 514
		}
515
			state.putTree(null, result);
399 516
	}
400
	
401
	
402
	private void handleVernaculars(MarkupImportState state, Element elVernacular, Taxon taxon) {
403
		verifyNoAttribute(elVernacular);
404
		verifyNoChildren(elVernacular, false);
405
		String value = elVernacular.getTextNormalize();
406
		Feature feature = Feature.COMMON_NAME();
407
		value = replaceStart(value, "Noms vernaculaires");
408
		String[] dialects = value.split(";");
409
		for (String singleDialect : dialects){
410
			handleSingleDialect(taxon, singleDialect, feature, state);
517
		save(result, state);
518
		return result;
411 519
		}
412
		return;
520

  
521
	private Classification createNewClassification(MarkupImportState state) {
522
		Classification result;
523
		result = Classification.NewInstance(state.getConfig().getClassificationTitle());
524
		state.putTree(null, result);
525
		return result;
413 526
	}
414 527

  
528
	private Taxon handleTaxon(MarkupImportState state, XMLEventReader reader, StartElement parentEvent) throws XMLStreamException {
529
		// TODO progress monitoring
530
		Map<String, Attribute> attributes = getAttributes(parentEvent);
531
		Taxon taxon = createTaxonAndName(state, attributes);
532
		state.setCurrentTaxon(taxon);
415 533

  
416
	private void handleSingleDialect(Taxon taxon, String singleDialect, Feature feature, MarkupImportState state) {
417
		singleDialect = singleDialect.trim();
418
		TaxonDescription description = getDescription(taxon);
419
		String reDialect = "\\(dial\\.\\s.*\\)";
420
//		String reDialect = "\\(.*\\)";
421
		Pattern patDialect = Pattern.compile(reDialect);
422
		Matcher matcher = patDialect.matcher(singleDialect);
423
		if (matcher.find()){
424
			String dialect = singleDialect.substring(matcher.start(), matcher.end());
425
			dialect = dialect.replace("(dial. ", "").replace(")", "");
534
		boolean hasTitle = false;
535
		boolean hasNomenclature = false;
536
		String taxonTitle = null;
537

  
538
		while (reader.hasNext()) {
539
			XMLEvent next = readNoWhitespace(reader);
540
			if (next.isEndElement()) {
541
				if (isMyEndingElement(next, parentEvent)) {
542
					checkMandatoryElement(hasTitle, parentEvent, TAXONTITLE);
543
					checkMandatoryElement(hasNomenclature, parentEvent,	NOMENCLATURE);
544
					handleUnexpectedAttributes(parentEvent.getLocation(),attributes);
426 545
			
427
			Language language = null;
546
					makeKeyNodes(state, parentEvent, taxonTitle);
547
					state.setCurrentTaxon(null);
548
					state.setCurrentTaxonNum(null);
549
					save(taxon, state);
550
					return taxon;
551
				} else {
552
					if (isEndingElement(next, HEADING)) {
553
						// NOT YET IMPLEMENTED
554
						popUnimplemented(next.asEndElement());
555
					} else if (isEndingElement(next, TEXT_SECTION)) {
556
						// NOT YET IMPLEMENTED
557
						popUnimplemented(next.asEndElement());
558
					} else if (isEndingElement(next, REFERENCES)) {
559
						// NOT YET IMPLEMENTED
560
						popUnimplemented(next.asEndElement());
561
					} else {
562
						handleUnexpectedEndElement(next.asEndElement());
563
					}
564
				}
565
			} else if (next.isStartElement()) {
566
				if (isStartingElement(next, HEADING)) {
567
					handleNotYetImplementedElement(next);
568
				} else if (isStartingElement(next, TAXONTITLE)) {
569
					taxonTitle = handleTaxonTitle(state, reader, next);
570
					hasTitle = true;
571
				} else if (isStartingElement(next, WRITER)) {
572
					makeKeyWriter(state, reader, taxon, taxonTitle, next);
573
				} else if (isStartingElement(next, TEXT_SECTION)) {
574
					handleNotYetImplementedElement(next);
575
				} else if (isStartingElement(next, KEY)) {
576
					handleKey(state, reader, next);
577
				} else if (isStartingElement(next, NOMENCLATURE)) {
578
					handleNomenclature(state, reader, next);
579
					hasNomenclature = true;
580
				} else if (isStartingElement(next, FEATURE)) {
581
					handleFeature(state, reader, next);
582
				} else if (isStartingElement(next, NOTES)) {
583
					// TODO is this the correct way to handle notes?
584
					String note = handleNotes(state, reader, next);
585

  
586
					UUID notesUuid;
428 587
			try {
429
				language = this.getLanguage(state, state.getTransformer().getLanguageUuid(dialect), dialect, dialect, dialect);
588
						notesUuid = state.getTransformer().getFeatureUuid(
589
								"notes");
590
						Feature feature = getFeature(state, notesUuid, "Notes",
591
								"Notes", "note", null);
592
						TextData textData = TextData.NewInstance(feature);
593
						textData.putText(Language.DEFAULT(), note);
594
						TaxonDescription description = getTaxonDescription(
595
								taxon, false, true);
596
						description.addElement(textData);
430 597
			} catch (UndefinedTransformerMethodException e) {
431
				logger.error(e.getMessage());
598
						String message = "getFeatureUuid method not yet implemented";
599
						fireWarningEvent(message, next, 8);
432 600
			}
433
			
434
			String commonNames = singleDialect.substring(0, matcher.start());
435
			String[] splitNames = commonNames.split(",");
436
			for (String commonNameString : splitNames){
437
				commonNameString = commonNameString.trim();
438
				CommonTaxonName commonName = CommonTaxonName.NewInstance(commonNameString, language);
439
				description.addElement(commonName);
601
				} else if (isStartingElement(next, REFERENCES)) {
602
					handleNotYetImplementedElement(next);
603
				} else if (isStartingElement(next, FIGURE)) {
604
					handleFigure(state, reader, next);
605
				} else if (isStartingElement(next, FOOTNOTE)) {
606
					FootnoteDataHolder footnote = handleFootnote(state, reader,
607
							next);
608
					if (footnote.isRef()) {
609
						String message = "Ref footnote not implemented here";
610
						fireWarningEvent(message, next, 4);
611
					} else {
612
						registerGivenFootnote(state, footnote);
440 613
			}
441
		}else{
442
			logger.warn("No dialect match: " +  singleDialect);
614
				} else {
615
					handleUnexpectedStartElement(next);
443 616
		}
617
			} else {
618
				handleUnexpectedElement(next);
444 619
	}
445

  
446

  
447
	private void handleReferences(MarkupImportState state, Element elReferences, Taxon taxon, AnnotatableEntity lastEntity) {
448
		verifyNoAttribute(elReferences);
449
		verifyNoChildren(elReferences, true);
450
		String refString = elReferences.getTextNormalize(); 
451
		if (lastEntity == null){
452
			logger.warn("No last entity defined: " + refString);
453
			return;
454 620
		}
455
		
456
		Annotation annotation = Annotation.NewInstance(refString, AnnotationType.EDITORIAL(), Language.DEFAULT());
457
		lastEntity.addAnnotation(annotation);
621
		throw new IllegalStateException("<Taxon> has no closing tag");
458 622
	}
459 623

  
460

  
461
	private PolytomousKey handleKeys(MarkupImportState state, Element elKey, Taxon taxon) {
462
		UnmatchedLeads openKeys = state.getUnmatchedLeads();
463
		
464
		//title
465
		String title = makeKeyTitle(elKey);
466
		
467
		//key
468
		PolytomousKey key = PolytomousKey.NewTitledInstance(title);
624
	private void makeKeyNodes(MarkupImportState state, XMLEvent event, String taxonTitle) {
625
		Taxon taxon = state.getCurrentTaxon();
626
		String num = state.getCurrentTaxonNum();
469 627
		
470
		//TODO add covered taxa etc.
471
		verifyNoAttribute(elKey);
628
		String nameString = CdmBase.deproxy(taxon.getName(), NonViralName.class).getNameCache();
629
//		String nameString = taxonTitle;
472 630
		
473
		//notes
474
		makeKeyNotes(elKey, key);
631
		//try to find matching lead nodes 
632
		UnmatchedLeadsKey leadsKey = UnmatchedLeadsKey.NewInstance(num, nameString);
633
		Set<PolytomousKeyNode> matchingNodes = handleMatchingNodes(state, taxon, leadsKey);
475 634
		
476
		//keycouplets
477
		List<Element> keychoices = new ArrayList<Element>();
478
		keychoices.addAll(elKey.getChildren("keycouplet"));
479
		keychoices.addAll(elKey.getChildren("keychoice"));
480
		
481
		
482
		for (Element elKeychoice : keychoices){
483
			handleKeyChoices(state, openKeys, key, elKeychoice, taxon);
484
			elKey.removeContent(elKeychoice);
635
		if (num != null){//same without using the num
636
			UnmatchedLeadsKey noNumLeadsKey = UnmatchedLeadsKey.NewInstance("", nameString);
637
			Set<PolytomousKeyNode> noNumMatchingNodes = handleMatchingNodes(state, taxon, noNumLeadsKey);
638
			if(noNumMatchingNodes.size() > 0){
639
				String message ="Taxon matches additional key node when not considering <num> attribute in taxontitle. This may be correct but may also indicate an error.";
640
				fireWarningEvent(message, event, 1);
641
			}
485 642
		}
486
		
487
		//
488
		verifyNoChildren(elKey);
489
		logger.info("Unmatched leads after key handling:" + openKeys.toString());
490
		
491

  
492
		if (state.getConfig().isDoPrintKeys()){
493
			key.print(System.err);
643
		//report missing match, if num exists
644
		if (matchingNodes.isEmpty() && num != null){
645
			String message = "Taxon has <num> attribute in taxontitle but no matching key nodes exist: %s, Key: %s";
646
			message = String.format(message, num, leadsKey.toString());
647
			fireWarningEvent(message, event, 1);
494 648
		}
495
		getPolytomousKeyService().save(key);
496
		return key;
497
	}
498

  
499

  
500
	/**
501
	 * @param state
502
	 * @param elKey
503
	 * @param openKeys
504
	 * @param key
505
	 * @param elKeychoice
506
	 * @param taxon 
507
	 */
508
	private void handleKeyChoices(MarkupImportState state, UnmatchedLeads openKeys, PolytomousKey key, Element elKeychoice, Taxon taxon) {
509
		
510
		//char Attribute
511
		//TODO it's still unclear if char is a feature and needs to be a new attribute 
512
		//or if it is handled as question. Therefore both cases are handled but feature
513
		//is finally not yet set
514
		KeyStatement question = handleKeychoiceChar(state, elKeychoice);
515
		Feature feature = handleKeychoiceCharAsFeature(state, elKeychoice);
516 649
		
517
		//lead
518
		List<PolytomousKeyNode> childNodes = handleKeychoiceLeads(state, key, elKeychoice, taxon, question, feature);
519
		
520
		//num -> match with unmatched leads
521
		handleKeychoiceNum(openKeys, key, elKeychoice, childNodes);
522

  
523
		//others
524
		verifyNoAttribute(elKeychoice);
525 650
	}
526

  
527

  
528
	/**
529
	 * @param openKeys
530
	 * @param key
531
	 * @param elKeychoice
532
	 * @param childNodes
533
	 */
534
	private void handleKeychoiceNum(UnmatchedLeads openKeys, PolytomousKey key, Element elKeychoice, List<PolytomousKeyNode> childNodes) {
535
		Attribute numAttr = elKeychoice.getAttribute("num");
536
		String num = CdmUtils.removeTrailingDot(numAttr == null? "":numAttr.getValue());
537
		UnmatchedLeadsKey okk = UnmatchedLeadsKey.NewInstance(key, num);
538
		Set<PolytomousKeyNode> matchingNodes = openKeys.getNodes(okk);
651
		
652
	private Set<PolytomousKeyNode> handleMatchingNodes(MarkupImportState state, Taxon taxon, UnmatchedLeadsKey leadsKey) {
653
		Set<PolytomousKeyNode> matchingNodes = state.getUnmatchedLeads().getNodes(leadsKey);
539 654
		for (PolytomousKeyNode matchingNode : matchingNodes){
540
			for (PolytomousKeyNode childNode : childNodes){
541
				matchingNode.addChild(childNode);
542
			}
543
			openKeys.removeNode(okk, matchingNode);
544
		}
545
		if (matchingNodes.isEmpty()){
546
			for (PolytomousKeyNode childNode : childNodes){
547
				key.getRoot().addChild(childNode);
548
			}
655
			state.getUnmatchedLeads().removeNode(leadsKey, matchingNode);
656
			matchingNode.setTaxon(taxon);
657
			state.getPolytomousKeyNodesToSave().add(matchingNode);
549 658
		}
550
		
551
		elKeychoice.removeAttribute("num");
659
		return matchingNodes;
552 660
	}
553 661

  
662
	private void handleKey(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent) throws XMLStreamException {
663
		// attributes
664
		Map<String, Attribute> attributes = getAttributes(parentEvent);
665
		String isSpotcharacters = getAndRemoveAttributeValue(attributes, IS_SPOTCHARACTERS);
666
		if (isNotBlank(isSpotcharacters) ) {
667
			//TODO isSpotcharacters
668
			String message = "Attribute isSpotcharacters not yet implemented for <key>";
669
			fireWarningEvent(message, parentEvent, 4);
670
		}
671
		
672
		PolytomousKey key = PolytomousKey.NewInstance();
673
		key.addTaxonomicScope(state.getCurrentTaxon());
674
		state.setCurrentKey(key);
675
		
676
		boolean isFirstCouplet = true;
677
		while (reader.hasNext()) {
678
			XMLEvent next = readNoWhitespace(reader);
679
			if (isMyEndingElement(next, parentEvent)) {
680
				save(key, state);
681
				state.setCurrentKey(null);
682
				return;
683
			} else if (isEndingElement(next, KEYNOTES)){
684
				popUnimplemented(next.asEndElement());
685
			} else if (isStartingElement(next, KEY_TITLE)) {
686
					handleKeyTitle(state, reader, next);
687
			} else if (isStartingElement(next, KEYNOTES)) {
688
				//TODO
689
				handleNotYetImplementedElement(next);
690
			} else if (isStartingElement(next, COUPLET)) {
691
				PolytomousKeyNode node = null;
692
				if (isFirstCouplet){
693
					node = key.getRoot();
694
					isFirstCouplet = false;
695
		}
696
				handleCouplet(state, reader, next, node);
697
			} else {
698
				handleUnexpectedElement(next);
699
	}
700
		}
701
		throw new IllegalStateException("<key> has no closing tag");
702
	}
554 703

  
555 704
	/**
556 705
	 * @param state
706
	 * @param reader
557 707
	 * @param key
558
	 * @param elKeychoice
559
	 * @param taxon
560
	 * @param feature
561
	 * @return
708
	 * @param next
709
	 * @throws XMLStreamException
562 710
	 */
563
	private List<PolytomousKeyNode> handleKeychoiceLeads(	MarkupImportState state, PolytomousKey key,	Element elKeychoice, Taxon taxon, KeyStatement question, Feature feature) {
564
		List<PolytomousKeyNode> childNodes = new ArrayList<PolytomousKeyNode>();
565
		List<Element> leads = elKeychoice.getChildren("lead");
566
		for(Element elLead : leads){
567
			PolytomousKeyNode childNode = handleLead(state, key, elLead, taxon, question, feature);
568
			childNodes.add(childNode);
569
		}
570
		return childNodes;
711
	private void handleKeyTitle(MarkupImportState state, XMLEventReader reader, XMLEvent next) throws XMLStreamException {
712
		PolytomousKey key = state.getCurrentKey();
713
		String keyTitle = getCData(state, reader, next);
714
		String standardTitles = "(?i)(Key\\sto\\sthe\\s(genera|species|varieties|forms))";
715
		
716
		if (isNotBlank(keyTitle) ){
717
			if (!state.getConfig().isReplaceStandardKeyTitles() || ! keyTitle.matches(standardTitles)){
718
				key.setTitleCache(keyTitle, true);
571 719
	}
572

  
573

  
574
	/**
575
	 * @param state
576
	 * @param elKeychoice
577
	 * @return
578
	 */
579
	private KeyStatement handleKeychoiceChar(MarkupImportState state, Element elKeychoice) {
580
		KeyStatement statement = null;
581
		Attribute charAttr = elKeychoice.getAttribute("char");
582
		if (charAttr != null){
583
			String charStr = charAttr.getValue();
584
			if (StringUtils.isNotBlank(charStr)){
585
				statement = KeyStatement.NewInstance(charStr);
586 720
			}
587
			elKeychoice.removeAttribute("char");
588
		}
589
		return statement;
590
	}
591
	
592
	/**
593
	 * @param state
594
	 * @param elKeychoice
595
	 * @return
596
	 */
597
	private Feature handleKeychoiceCharAsFeature(MarkupImportState state, Element elKeychoice) {
598
		Feature feature = null;
599
		Attribute charAttr = elKeychoice.getAttribute("char");
600
		if (charAttr != null){
601
			String charStr = charAttr.getValue();
602
			feature = getFeature(charStr, state);
603
			elKeychoice.removeAttribute("char");
604 721
		}
605
		return feature;
606
	}
607

  
608 722

  
609
	private PolytomousKeyNode handleLead(MarkupImportState state, PolytomousKey key, Element elLead, Taxon taxon, KeyStatement question, Feature feature) {
610
		PolytomousKeyNode node = PolytomousKeyNode.NewInstance();
611
		//TODO the char attribute in the keychoice is more a feature than a question
612
		//needs to be discussed on model side
613
		node.setQuestion(question);
614
//		node.setFeature(feature);
615
		
616
		//text
617
		String text = handleLeadText(elLead, node);
618
		
619
		//num
620
		handleLeadNum(elLead, text);
723
	private void handleCouplet(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, PolytomousKeyNode parentNode) throws XMLStreamException {
724
		String num = getOnlyAttribute(parentEvent, NUM, true);
725
		List<PolytomousKeyNode> childList = new ArrayList<PolytomousKeyNode>(); 
621 726
		
622
		//goto
623
		handleLeadGoto(state, key, elLead, taxon, node);
624
		
625
		//others
626
		verifyNoAttribute(elLead);
627
		
628
		return node;
727
		while (reader.hasNext()) {
728
			XMLEvent next = readNoWhitespace(reader);
729
			if (isMyEndingElement(next, parentEvent)) {
730
				completeCouplet(state, parentEvent, parentNode, num, childList);
731
				return;
732
			} else if (isStartingElement(next, QUESTION)) {
733
				handleQuestion(state, reader, next, childList);
734
			} else if (isStartingElement(next, KEYNOTES)) {
735
				//TODO
736
				handleNotYetImplementedElement(next);
737
			} else if (isEndingElement(next, KEYNOTES)) {
738
				//TODO
739
				popUnimplemented(next.asEndElement());
740
			} else {
741
				handleUnexpectedElement(next);
629 742
	}
630

  
631

  
632
	/**
633
	 * @param elLead
634
	 * @param node
635
	 * @return
636
	 */
637
	private String handleLeadText(Element elLead, PolytomousKeyNode node) {
638
		String text = elLead.getAttributeValue("text").trim();
639
		if (StringUtils.isBlank(text)){
640
			logger.warn("Empty text in lead");
641
		}
642
		elLead.removeAttribute("text");
643
		KeyStatement statement = KeyStatement.NewInstance(text);
644
		node.setStatement(statement);
645
		return text;
743
		}
744
		throw new IllegalStateException("<couplet> has no closing tag");
646 745
	}
647 746

  
648

  
649 747
	/**
650 748
	 * @param state
651
	 * @param key
652
	 * @param elLead
653
	 * @param taxon
654
	 * @param node
749
	 * @param parentEvent
750
	 * @param parentNode
751
	 * @param num
752
	 * @param childList
655 753
	 */
656
	private void handleLeadGoto(MarkupImportState state, PolytomousKey key, Element elLead, Taxon taxon, PolytomousKeyNode node) {
657
		Attribute gotoAttr = elLead.getAttribute("goto");
658
		if (gotoAttr != null){
659
			String strGoto = gotoAttr.getValue().trim();
660
			//create key
661
			UnmatchedLeadsKey gotoKey = null;
662
			if (isInternalNode(strGoto)){
663
				gotoKey = UnmatchedLeadsKey.NewInstance(key, strGoto);
664
			}else{
665
				String taxonKey = makeTaxonKey(strGoto, taxon);
666
				gotoKey = UnmatchedLeadsKey.NewInstance(taxonKey);
667
			}
668
			//
669
			UnmatchedLeads openKeys = state.getUnmatchedLeads();
670
			if (gotoKey.isInnerLead()){
671
				Set<PolytomousKeyNode> existingNodes = openKeys.getNodes(gotoKey);
672
				for (PolytomousKeyNode existingNode : existingNodes){
673
					node.addChild(existingNode);
674
				}
754
	private void completeCouplet(MarkupImportState state, XMLEvent parentEvent,
755
			PolytomousKeyNode parentNode, String num,
756
			List<PolytomousKeyNode> childList) {
757
		if (parentNode != null){
758
			for (PolytomousKeyNode childNode : childList){
759
				parentNode.addChild(childNode);
675 760
			}
676
			openKeys.addKey(gotoKey, node);
677
			//remove attribute (need for consistency check)
678
			elLead.removeAttribute("goto");
679
		}else{
680
			logger.warn("lead has no goto attribute");
761
		}else if (isNotBlank(num)){
762
			UnmatchedLeadsKey unmatchedKey = UnmatchedLeadsKey.NewInstance(state.getCurrentKey(), num);
763
			Set<PolytomousKeyNode> nodes = state.getUnmatchedLeads().getNodes(unmatchedKey);
764
			for(PolytomousKeyNode nodeToMatch: nodes){
765
				for (PolytomousKeyNode childNode : childList){
766
					nodeToMatch.addChild(childNode);
681 767
		}
768
				state.getUnmatchedLeads().removeNode(unmatchedKey, nodeToMatch);
682 769
	}
683

  
684

  
685
	/**
686
	 * @param elLead
687
	 * @param text
688
	 */
689
	private void handleLeadNum(Element elLead, String text) {
690
		Attribute numAttr = elLead.getAttribute("num");
691
		if (numAttr != null){
692
			//TODO num
693
			String num = numAttr.getValue();
694
			elLead.removeAttribute("num");
695 770
		}else{
696
			logger.info("Keychoice has no num attribute: " + text);
771
			String message = "Parent num could not be matched. Please check if num (%s) is correct";
772
			message = String.format(message, num);
773
			fireWarningEvent(message, parentEvent, 6);
697 774
		}
698 775
	}
699 776

  
777
	private void handleQuestion(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, List<PolytomousKeyNode> nodesList) throws XMLStreamException {
778
		// attributes
779
		Map<String, Attribute> attributes = getAttributes(parentEvent);
780
		//needed only for data lineage
781
		String questionNum = getAndRemoveRequiredAttributeValue(parentEvent, attributes, NUM);
782
		
783
		PolytomousKeyNode myNode = PolytomousKeyNode.NewInstance();
784
		myNode.setKey(state.getCurrentKey());  //to avoid NPE while computing num in PolytomousKeyNode in case this node is not matched correctly with a parent
785
		nodesList.add(myNode);
786
		
787
		while (reader.hasNext()) {
788
			XMLEvent next = readNoWhitespace(reader);
789
			if (isMyEndingElement(next, parentEvent)) {
790
				return;
791
			} else if (isStartingElement(next, TEXT)) {
792
				String text = getCData(state, reader, next);
793
				KeyStatement statement = KeyStatement.NewInstance(text);
794
				myNode.setStatement(statement);
795
			} else if (isStartingElement(next, COUPLET)) {
796
				//TODO test
797
				handleCouplet(state, reader, next, myNode);
798
			} else if (isStartingElement(next, TO_COUPLET)) {
799
				handleToCouplet(state, reader, next, myNode);
800
			} else if (isStartingElement(next, TO_TAXON)) {
801
				handleToTaxon(state, reader, next, myNode);
802
		
803
			} else if (isStartingElement(next, TO_KEY)) {
804
				//TODO
805
				handleNotYetImplementedElement(next);
806
			} else if (isEndingElement(next, TO_KEY)){
807
				//TODO
808
				popUnimplemented(next.asEndElement());
809
			} else if (isStartingElement(next, KEYNOTES)) {
810
				//TODO
811
				handleNotYetImplementedElement(next);
812
			} else if (isEndingElement(next, KEYNOTES)){
813
				//TODO
814
				popUnimplemented(next.asEndElement());
815
			} else {
816
				handleUnexpectedElement(next);
817
	}
818
		}
819
		throw new IllegalStateException("<question> has no closing tag");
820
	}
821

  
822
	private void handleToCouplet(MarkupImportState state, XMLEventReader reader, XMLEvent next, PolytomousKeyNode node) throws XMLStreamException {
823
		String num = getOnlyAttribute(next, NUM, true);
824
		String cData = getCData(state, reader, next, false);
825
		if (isNotBlank(cData) && ! cData.equals(num)){
826
			String message = "CData ('%s') not handled in <toCouplet>";
827
			message = String.format(message, cData);
828
			fireWarningEvent(message, next, 4);
829
			}
830
		UnmatchedLeadsKey unmatched = UnmatchedLeadsKey.NewInstance(state.getCurrentKey(), num);
831
		state.getUnmatchedLeads().addKey(unmatched, node);
832
				}
700 833

  
834
	private void handleToTaxon(MarkupImportState state, XMLEventReader reader, XMLEvent parentEvent, PolytomousKeyNode node) throws XMLStreamException {
835
		Map<String, Attribute> attributes = getAttributes(parentEvent);
836
		String num = getAndRemoveAttributeValue(attributes, NUM);
837
		String taxonStr = getCData(state, reader, parentEvent, false);
838
		//TODO ?
839
		taxonStr = makeTaxonKey(taxonStr, state.getCurrentTaxon());
840
		UnmatchedLeadsKey unmatched = UnmatchedLeadsKey.NewInstance(num, taxonStr);
841
		state.getUnmatchedLeads().addKey(unmatched, node);
842
		return;
843
		}
844
	
701 845
	private String makeTaxonKey(String strGoto, Taxon taxon) {
702 846
		String result = "";
703 847
		if (strGoto == null){
704 848
			return "";
705 849
		}
706
		String strGenusName = CdmBase.deproxy(taxon.getName(), NonViralName.class).getGenusOrUninomial();
850
		
851
		NonViralName<?> name = CdmBase.deproxy(taxon.getName(), NonViralName.class);
852
		String strGenusName = name.getGenusOrUninomial();
853
		
854
		
707 855
		strGoto = strGoto.replaceAll("\\([^\\(\\)]*\\)", "");  //replace all brackets
708 856
		strGoto = strGoto.replaceAll("\\s+", " "); //replace multiple whitespaces by exactly one whitespace
709 857
		
......
714 862
			if (isGenusAbbrev(single, strGenusName)){
715 863
				split[i] = strGenusName;
716 864
			}
717
//			if (isInfraSpecificMarker(single)){
718
//				String strSpeciesName = CdmBase.deproxy(taxon.getName(), NonViralName.class).getSpecificEpithet();
719
//				split[i] = strGenusName + " " + strSpeciesName + " ";
720
//			}
865
			if (isInfraSpecificMarker(single)){
866
				String strSpeciesEpi = name.getSpecificEpithet();
867
				if (isBlank(result)){
868
					result += strGenusName + " " + strSpeciesEpi;
869
				}
870
			}
721 871
			result = (result + " " + split[i]).trim();
722 872
		}
723 873
		return result;
724 874
	}
725

  
875
	
726 876

  
727 877
	private boolean isInfraSpecificMarker(String single) {
728 878
		try {
729 879
			if (Rank.getRankByAbbreviation(single).isInfraSpecific()){
730 880
				return true;
881
			}else{
882
				return false;
731 883
			}
732 884
		} catch (UnknownCdmTypeException e) {
733 885
			return false;
734 886
		}
735
		return false;
736 887
	}
737

  
738

  
888
	
739 889
	private boolean isGenusAbbrev(String single, String strGenusName) {
740 890
		if (! single.matches("[A-Z]\\.?")) {
741 891
			return false;
......
746 896
		}
747 897
	}
748 898

  
749

  
750
	private boolean isInternalNode(String strGoto) {
751
		return CdmUtils.isNumeric(strGoto);
752
	}
753

  
754

  
755
	private void makeKeyNotes(Element keyElement, PolytomousKey key) {
756
		Element elNotes = keyElement.getChild("notes");
757
		if (elNotes != null){
758
			keyElement.removeContent(elNotes);
759
			String notes = elNotes.getTextNormalize();
760
			if (StringUtils.isNotBlank(notes)){
761
				key.addAnnotation(Annotation.NewInstance(notes, AnnotationType.EDITORIAL(), Language.DEFAULT()));
899
	/**
900
	 * @param state
901
	 * @param reader
902
	 * @param taxon
903
	 * @param taxonTitle
904
	 * @param next
905
	 * @throws XMLStreamException
906
	 */
907
	private void makeKeyWriter(MarkupImportState state, XMLEventReader reader, Taxon taxon, String taxonTitle, XMLEvent next) throws XMLStreamException {
908
		WriterDataHolder writer = handleWriter(state, reader, next);
909
		taxon.addExtension(writer.extension);
910
		// TODO what if taxonTitle comes later
911
		if (StringUtils.isNotBlank(taxonTitle)
912
				&& writer.extension != null) {
913
			Reference<?> sec = ReferenceFactory.newBookSection();
914
			sec.setTitle(taxonTitle);
915
			TeamOrPersonBase<?> author = createAuthor(writer.writer);
916
			sec.setAuthorTeam(author);
917
			sec.setInReference(state.getConfig()
918
					.getSourceReference());
919
			taxon.setSec(sec);
920
			registerFootnotes(state, sec, writer.footnotes);
921
		} else {
922
			String message = "No taxontitle exists for writer";
923
			fireWarningEvent(message, next, 6);
924
	}
925
	}
926

  
927
	private String handleNotes(MarkupImportState state, XMLEventReader reader,
928
			XMLEvent parentEvent) throws XMLStreamException {
929
		checkNoAttributes(parentEvent);
930

  
931
		String text = "";
932
		while (reader.hasNext()) {
933
			XMLEvent next = readNoWhitespace(reader);
934
			if (isMyEndingElement(next, parentEvent)) {
935
				return text;
936
			} else if (next.isEndElement()) {
937
				if (isEndingElement(next, HEADING)) {
938
					popUnimplemented(next.asEndElement());
939
				} else if (isEndingElement(next, WRITER)) {
940
					popUnimplemented(next.asEndElement());
941
				} else if (isEndingElement(next, NUM)) {
942
					popUnimplemented(next.asEndElement());
943
				} else {
944
					handleUnexpectedEndElement(next.asEndElement());
945
			}
946
			} else if (next.isStartElement()) {
947
				if (isStartingElement(next, HEADING)) {
948
					handleNotYetImplementedElement(next);
949
				} else if (isStartingElement(next, SUB_HEADING)) {
950
					String subheading = getCData(state, reader, next).trim();
951
					if (! isNoteHeading(subheading)) {
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff