Project

General

Profile

Download (48 KB) Statistics
| Branch: | Revision:
1
/**
2
* Copyright (C) 2007 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.eflora.centralAfrica.ferns;
11

    
12
import java.sql.ResultSet;
13
import java.sql.SQLException;
14
import java.util.Arrays;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Set;
20
import java.util.UUID;
21

    
22
import org.apache.commons.lang.StringUtils;
23
import org.apache.log4j.Logger;
24
import org.springframework.stereotype.Component;
25

    
26
import eu.etaxonomy.cdm.api.service.IClassificationService;
27
import eu.etaxonomy.cdm.common.CdmUtils;
28
import eu.etaxonomy.cdm.io.common.IOValidator;
29
import eu.etaxonomy.cdm.io.common.TdwgAreaProvider;
30
import eu.etaxonomy.cdm.io.common.mapping.DbImportMapping;
31
import eu.etaxonomy.cdm.io.common.mapping.DbImportMethodMapper;
32
import eu.etaxonomy.cdm.io.common.mapping.DbImportTaxIncludedInMapper;
33
import eu.etaxonomy.cdm.io.common.mapping.IMappingImport;
34
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
35
import eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns.validation.CentralAfricaFernsTaxonImportValidator;
36
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
37
import eu.etaxonomy.cdm.model.common.CdmBase;
38
import eu.etaxonomy.cdm.model.common.Language;
39
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
40
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
41
import eu.etaxonomy.cdm.model.description.Distribution;
42
import eu.etaxonomy.cdm.model.description.Feature;
43
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTermBase;
44
import eu.etaxonomy.cdm.model.description.PresenceTerm;
45
import eu.etaxonomy.cdm.model.description.TaxonDescription;
46
import eu.etaxonomy.cdm.model.description.TextData;
47
import eu.etaxonomy.cdm.model.location.Country;
48
import eu.etaxonomy.cdm.model.location.NamedArea;
49
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
50
import eu.etaxonomy.cdm.model.location.NamedAreaType;
51
import eu.etaxonomy.cdm.model.name.BotanicalName;
52
import eu.etaxonomy.cdm.model.name.NonViralName;
53
import eu.etaxonomy.cdm.model.name.Rank;
54
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
55
import eu.etaxonomy.cdm.model.reference.Reference;
56
import eu.etaxonomy.cdm.model.taxon.Classification;
57
import eu.etaxonomy.cdm.model.taxon.Synonym;
58
import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
59
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
60
import eu.etaxonomy.cdm.model.taxon.Taxon;
61
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
62
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
63

    
64

    
65
/**
66
 * @author a.mueller
67
 */
68

    
69
@Component
70
public class CentralAfricaFernsTaxonRelationImport  extends CentralAfricaFernsImportBase<TaxonBase> implements IMappingImport<TaxonBase, CentralAfricaFernsImportState>{
71
	private static final Logger logger = Logger.getLogger(CentralAfricaFernsTaxonRelationImport.class);
72
	
73
	private DbImportMapping<?,?> mapping;
74
	
75
	
76
	private static final String pluralString = "taxon relations";
77
	private static final String dbTableName = "[African pteridophytes]";
78
	private static final Class<?> cdmTargetClass = TaxonBase.class;
79

    
80
	private Map<String, UUID> nameCacheTaxonMap = new HashMap<String, UUID>();
81
	private Map<String, UUID> titleCacheTaxonMap = new HashMap<String, UUID>();
82

    
83
	private CentralAfricaFernsImportState state;
84

    
85
	
86
	public CentralAfricaFernsTaxonRelationImport(){
87
		super(pluralString, dbTableName, cdmTargetClass);
88
	}
89

    
90
	
91
	@Override
92
	protected String getIdQuery() {
93
		String strQuery = " SELECT [Taxon number] FROM " + dbTableName;;
94
		return strQuery;
95
	}
96

    
97
	@Override
98
	protected DbImportMapping<?,?> getMapping() {
99
		if (mapping == null){
100
			mapping = new DbImportMapping();
101
			
102
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "createObject", ResultSet.class, CentralAfricaFernsImportState.class));
103
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapCommonName", ResultSet.class, CentralAfricaFernsImportState.class));
104
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapDistribution", ResultSet.class, CentralAfricaFernsImportState.class ));
105
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapEcology", ResultSet.class, CentralAfricaFernsImportState.class));
106

    
107
		}
108
		return mapping;
109
	}
110

    
111
	/* (non-Javadoc)
112
	 * @see eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns.CentralAfricaFernsImportBase#getRecordQuery(eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns.CentralAfricaFernsImportConfigurator)
113
	 */
114
	@Override
115
	protected String getRecordQuery(CentralAfricaFernsImportConfigurator config) {
116
		String strSelect = " SELECT * ";
117
		String strFrom = " FROM [African pteridophytes] as ap";
118
		String strWhere = " WHERE ( ap.[taxon number] IN (" + ID_LIST_TOKEN + ") )";
119
		String strOrderBy = " ORDER BY [Taxon number]";
120
		String strRecordQuery = strSelect + strFrom + strWhere + strOrderBy ;
121
		return strRecordQuery;
122
	}
123
	
124

    
125
	/* (non-Javadoc)
126
	 * @see eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns.CentralAfricaFernsImportBase#doInvoke(eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns.CentralAfricaFernsImportState)
127
	 */
128
	@Override
129
	protected void doInvoke(CentralAfricaFernsImportState state) {
130
		this.state = state;
131
		fillTaxonMap();
132
		super.doInvoke(state);
133
		return;
134
	}
135

    
136

    
137
	/**
138
	 * Fills the nameCache and the titleCache maps. The maps are used to find existing taxa
139
	 * by titleCache or nameCache matching.
140
	 * Matching may be implemented more sophisticated in future versions.
141
	 */
142
	private void fillTaxonMap() {
143
		List<String> propPath = Arrays.asList(new String []{"name"});
144
		
145
		List<Taxon> taxonList = (List)getTaxonService().list(Taxon.class, null, null, null, propPath );
146
		for (Taxon taxon : taxonList){
147
			NonViralName<?> nvn = CdmBase.deproxy(taxon.getName(), NonViralName.class);
148
			UUID uuid = taxon.getUuid();
149
			String nameCache = nvn.getNameCache();
150
			String titleCache = nvn.getTitleCache();
151
			nameCacheTaxonMap.put(nameCache, uuid);
152
			titleCacheTaxonMap.put(titleCache, uuid);
153
		}
154
	}
155

    
156

    
157
	@Override
158
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, CentralAfricaFernsImportState state) {
159
		String nameSpace;
160
		Class<?> cdmClass;
161
		Set<String> idSet;
162
		Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<Object, Map<String, ? extends CdmBase>>();
163
		
164
		try{
165
			Set<String> taxonIdSet = new HashSet<String>();
166
//				Set<String> referenceIdSet = new HashSet<String>();
167
			while (rs.next()){
168
				handleForeignKey(rs, taxonIdSet, "Current");
169
				handleForeignKey(rs, taxonIdSet, "Taxon number");
170
//				handleForeignKey(rs, referenceIdSet, "PTRefFk");
171
			}
172

    
173
			//taxon map
174
			nameSpace = TAXON_NAMESPACE;
175
			cdmClass = TaxonBase.class;
176
			Map<String, TaxonBase> taxonMap = (Map<String, TaxonBase>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, taxonIdSet, nameSpace);
177
			result.put(nameSpace, taxonMap);
178
				
179
				
180
			//reference map
181
			this.sourceReference = getFernsSourceReference(state);
182
//			nameSpace = "Reference";
183
//			cdmClass = Reference.class;
184
//			Map<String, Person> referenceMap = (Map<String, Person>)getCommonService().getSourcedObjectsByIdInSource(Person.class, teamIdSet, nameSpace);
185
//			result.put(Reference.class, referenceMap);
186

    
187
		} catch (SQLException e) {
188
			throw new RuntimeException(e);
189
		}
190
		return result;
191
	}
192
	
193

    
194
	/* (non-Javadoc)
195
	 * @see eu.etaxonomy.cdm.io.common.mapping.IMappingImport#createObject(java.sql.ResultSet, eu.etaxonomy.cdm.io.common.ImportStateBase)
196
	 */
197
	@Override
198
	public TaxonBase createObject(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException {
199
		TaxonBase<?> result = null;
200
		try {
201
			String status = rs.getString("Current/Synonym");
202
			String taxonNumber = rs.getString("Taxon number");
203
			state.setTaxonNumber(taxonNumber);
204
			if ("s".equalsIgnoreCase(status)){
205
				//synonym
206
				result = handleSynonym(rs, state);
207
			}else{
208
				//accepted Taxon
209
				result = handleTaxon(rs, state);
210
			}
211
			
212
			return result;
213
		} catch (Exception e) {
214
			e.printStackTrace();
215
			return result;
216
		}
217

    
218
	}
219

    
220
	
221
	/**
222
	 * Class to store all epithets of the database record. Maybe extended with business logic.
223
	 */
224
	private class Epithets{
225
		private String orderName;
226
		private String subOrderName;
227
		private String familyName;
228
		private String subFamilyName;
229
		private String tribusName;
230
		private String subTribusName;
231
		private String sectionName;
232
		private String subsectionName;
233
		private String genusName;
234
		private String subGenusName;
235
		private String seriesName;
236
		private String specificEpithet;
237
		private String subspeciesName;
238
		private String varietyName;
239
		private String subVariety;
240
		private String formaName;
241
		private String subFormaName;
242
	}
243

    
244
	
245
	/**
246
	 * Handles records with status synonym. A synonym relationship to the accepted taxon
247
	 * is created.
248
	 * @param rs
249
	 * @param state
250
	 * @return
251
	 * @throws SQLException
252
	 */
253
	private Synonym handleSynonym(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException {
254
		String accTaxonId = rs.getString("Current");
255
		String nomRemarksString = rs.getString("Current/Synonym");
256
		
257
		String synonymId = state.getTaxonNumber();
258
		Synonym synonym = (Synonym)state.getRelatedObject(TAXON_NAMESPACE, synonymId);
259
		if (synonym == null){
260
			logger.warn ("Synonym ("+synonymId+")not found.");
261
			return null;
262
		}
263
		TaxonBase<?> taxonBase = CdmBase.deproxy(state.getRelatedObject(TAXON_NAMESPACE, accTaxonId), TaxonBase.class);
264
			
265
		if (taxonBase != null){
266
			if (taxonBase.isInstanceOf(Taxon.class)){
267
				Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
268
				SynonymRelationship rel = taxon.addSynonym(synonym, SynonymRelationshipType.SYNONYM_OF());
269
				if ("p.p.".equalsIgnoreCase(nomRemarksString)){
270
					rel.setProParte(true);
271
				}
272
			}else{
273
				logger.warn("Accepted taxon (" + accTaxonId + ") for synonym (" + synonymId +") is not of type 'Current'");
274
			}		
275
		}else{
276
			logger.warn("Taxon (" + accTaxonId + ") not found for synonym (" + synonymId +")");
277
		}
278
		
279
		return synonym;
280
	}
281

    
282
	
283
	/**
284
	 * Handles all records with status 'current'. Creates parent-child relationships to the 
285
	 * higher taxa. Uses a complex algorithm to reuse existing higher taxa.
286
	 * @param rs
287
	 * @param state
288
	 * @return
289
	 * @throws SQLException
290
	 */
291
	private Taxon handleTaxon(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException {
292
		String taxonNumber = rs.getString("Taxon number");
293
		Taxon child = (Taxon)state.getRelatedObject(TAXON_NAMESPACE, taxonNumber);
294
		if (child == null){
295
			logger.warn("Taxon does not exist: " + taxonNumber);
296
			return null;
297
		}
298
		Epithets epithets = new Epithets();
299
		epithets.orderName = rs.getString("Order name");
300
		epithets.subOrderName = rs.getString("Suborder name");
301
		epithets.familyName = rs.getString("Family name");
302
		epithets.subFamilyName = rs.getString("Subfamily name");
303
		epithets.tribusName = rs.getString("Tribus name");
304
		epithets.subTribusName = rs.getString("Subtribus name");
305
		epithets.sectionName = rs.getString("Section name");
306
		epithets.subsectionName = rs.getString("Subsection name");
307
		epithets.genusName = rs.getString("Genus name");
308
		epithets.subGenusName = rs.getString("Subgenus name");
309
		epithets.seriesName = rs.getString("Series name");
310
		epithets.specificEpithet = rs.getString("Specific epihet");
311
		epithets.subspeciesName = rs.getString("Subspecies name");
312
		epithets.varietyName = rs.getString("Variety name");
313
		epithets.subVariety = rs.getString("Subvariery");
314
		epithets.formaName = rs.getString("Forma name");
315
		epithets.subFormaName = rs.getString("Subforma");
316
		
317
		makeNextHigherTaxon(state, rs, child, epithets);
318
		return child;
319
	}
320

    
321

    
322
	/**
323
	 * Adds recursively this taxon to the next higher taxon. If the taxon exists already 
324
	 * the relationship is not added again.<BR>
325
	 * If the author is missing in the old taxon but not in the new taxon the 
326
	 * old taxon will get the new taxons author.(NOT VALID ANY MORE)<BR> 
327
	 * If authors differ a new taxon is created.<BR>
328
	 * If a higher taxon exists the method is called recursively on this taxon.
329
	 * @throws SQLException 
330
	 */
331
	private void makeNextHigherTaxon(CentralAfricaFernsImportState state, ResultSet rs, Taxon child, Epithets epithets) throws SQLException {
332

    
333
		Taxon constructedHigherTaxon = constructNextHigherTaxon(state, rs, child, epithets);
334
		Reference<?> citation = null;
335
		String microcitation = null;
336
		
337
		if (constructedHigherTaxon != null){
338
			handleHigherTaxonMustExist(state, rs, child, epithets, constructedHigherTaxon, citation, microcitation);
339
		}else{
340
			//add taxon to tree if not yet added
341
			if (child.getTaxonNodes().size() == 0){
342
				makeTaxonomicallyIncluded(state, null, child, null, citation, microcitation);
343
			}
344
		}
345
	}
346

    
347

    
348

    
349
	/**
350
	 * Handles the case when the database record has data for a taxon of a higher rank
351
	 * than the <code>child</code> taxon's rank.
352
	 * @param state
353
	 * @param rs
354
	 * @param child
355
	 * @param epithets
356
	 * @param higherTaxon
357
	 * @param citation
358
	 * @param microcitation
359
	 * @throws SQLException
360
	 */
361
	private void handleHigherTaxonMustExist(CentralAfricaFernsImportState state, ResultSet rs, Taxon child, Epithets epithets, Taxon constructedHigherTaxon, Reference<?> citation, String microCitation) throws SQLException {
362
		Taxon parentTaxon = getParent(child);
363
		if (parentTaxon == null){
364
			//if no parent taxon exists
365
			Taxon existingTaxon = findExistingNonParentTaxon(state, constructedHigherTaxon);
366
			if (existingTaxon != null){
367
				//a taxon with same title cache or same name cache exists
368
				parentTaxon = mergeExistingAndConstructedTaxon(state, existingTaxon, constructedHigherTaxon);
369
			}else{
370
				parentTaxon = constructedHigherTaxon;
371
			}
372
			makeTaxonomicallyIncluded(state, null, child, parentTaxon, citation, microCitation);
373
		}else{
374
			//parent taxon exists
375
			if (namesMatch(parentTaxon, constructedHigherTaxon)){
376
				//parents match
377
				//TODO what if the higher taxonomy does not match
378
				parentTaxon = mergeExistingAndConstructedTaxon(state, parentTaxon, constructedHigherTaxon);
379
			}else if (compareRanks(parentTaxon, constructedHigherTaxon) != 0){
380
				//ranks unequal
381
				parentTaxon = handleUnequalRanks(parentTaxon, constructedHigherTaxon);
382
			}else if (! nameCachesMatch(parentTaxon, constructedHigherTaxon)){
383
				//nameCache not equal
384
				parentTaxon = handleUnequalNameCaches(parentTaxon, constructedHigherTaxon);
385
			}else if (! authorsMatch(parentTaxon, constructedHigherTaxon)){
386
				//nameCache not equal
387
				parentTaxon = handleUnequalAuthors(parentTaxon, constructedHigherTaxon);
388
			}
389
		}
390
		//save the parent taxon, if it is new
391
		if (parentTaxon == constructedHigherTaxon){
392
			saveConstructedTaxon(state, constructedHigherTaxon);
393
		}
394
		makeNextHigherTaxon(state, rs, parentTaxon, epithets);
395
	}
396
	
397

    
398
	/**
399
	 * Merges author information of the constructed taxon into the existing taxon.
400
	 * Returns the existing taxon.
401
	 * @param state 
402
	 * @param parentTaxon
403
	 * @param constructedHigherTaxon
404
	 */
405
	private Taxon mergeExistingAndConstructedTaxon(CentralAfricaFernsImportState state, Taxon existingTaxon, Taxon constructedTaxon) {
406
		NonViralName<?> constructedName = CdmBase.deproxy(constructedTaxon.getName(), NonViralName.class);
407
		NonViralName<?> existingName = CdmBase.deproxy(existingTaxon.getName(), NonViralName.class);
408
		if (constructedName.hasAuthors()){
409
			if (! existingName.hasAuthors()){
410
				logger.warn(state.getTaxonNumber() + " - Constrcucted name ("+constructedName.getTitleCache()+") has authors but existing name ("+existingName.getTitleCache()+") has no authors");
411
			}else if (! authorsMatch(constructedName, existingName)){
412
				logger.warn(state.getTaxonNumber() + " - Constrcucted name ("+constructedName.getTitleCache()+") and existing name ("+existingName.getTitleCache()+") have different authors");
413
			}else {
414
				//authors match and are not null
415
			}
416
		}
417
		// more?
418
		return existingTaxon;
419
	}
420

    
421

    
422
	/**
423
	 * Strategy for the decision if an existing parent or a constructed higher taxon should 
424
	 * be taken as parent in case that the authors of the name differ somehow.
425
	 * Current strategy: use existing parent if constructed higher taxon has no authors 
426
	 * at all. Use constructed taxon otherwise.
427
	 * @param existingParentTaxon
428
	 * @param constructedHigherTaxon
429
	 * @return
430
	 */
431
	private Taxon handleUnequalAuthors(Taxon existingParentTaxon, Taxon constructedHigherTaxon) {
432
		Taxon result;
433
		BotanicalName existingName = CdmBase.deproxy(existingParentTaxon.getName(), BotanicalName.class);
434
		BotanicalName constructedName = (BotanicalName)constructedHigherTaxon.getName();
435
		//current strategy: if constructedName has no authors (and parentName has
436
		if (! constructedName.hasAuthors()){
437
			result = existingParentTaxon;
438
		}else if (! existingName.hasAuthors()){
439
			result = constructedHigherTaxon;
440
		}else{
441
			result = constructedHigherTaxon;
442
		}
443
		return result;
444
	}
445

    
446
	/**
447
	 * Strategy for the decision if an existing parent or a constructed higher taxon 
448
	 * should be taken as parent in case that the name caches differ somehow.
449
	 * Current strategy: Not implemented. Always use constructed higher taxon.
450
	 * @param existingParentTaxon
451
	 * @param constructedHigherTaxon
452
	 * @return
453
	 */
454
	private Taxon handleUnequalNameCaches(Taxon parentTaxon, Taxon constructedHigherTaxon) {
455
		BotanicalName parentName = CdmBase.deproxy(parentTaxon.getName(), BotanicalName.class);
456
		BotanicalName constructedName = (BotanicalName)constructedHigherTaxon.getName();
457
		logger.warn("handleUnequalNameCaches not yet implemented");
458
		return constructedHigherTaxon;
459
	}
460

    
461

    
462
	/**
463
	 * Handles the case that the existing parent taxon and the constructed parent taxon
464
	 * have a diffent rank. Returns the constructedHigherTaxon if no common grand parent exists. 
465
	 * @param parentTaxon
466
	 * @param constructedHigherTaxon
467
	 * @return
468
	 */
469
	private Taxon handleUnequalRanks(Taxon parentTaxon, Taxon constructedHigherTaxon) {
470
		BotanicalName parentName = CdmBase.deproxy(parentTaxon.getName(), BotanicalName.class);
471
		BotanicalName constructedName = (BotanicalName)constructedHigherTaxon.getName();
472
		int compare = compareRanks(parentName, constructedName);
473
		Taxon lowerTaxon = parentTaxon;
474
		Taxon grandParentTaxon = constructedHigherTaxon;
475
		if (compare < 0){
476
			lowerTaxon = constructedHigherTaxon;
477
			grandParentTaxon = parentTaxon;
478
		}	
479
		Taxon commonGrandParent = checkIsGrandParent(lowerTaxon, grandParentTaxon);
480
		if (commonGrandParent != null){
481
			if (lowerTaxon == constructedHigherTaxon){
482
				//TODO merge 
483
				logger.warn("Merge in between taxon not yet implemented");
484
			}
485
		}else{
486
			return constructedHigherTaxon;
487
		}
488
		return lowerTaxon;
489
	}
490

    
491
	/**
492
	 * Tries to find a taxon which matches the constructed taxon but is not a parent
493
	 * taxon of the constructed taxon's child (condition will not be checked).
494
	 * Returns null if no such taxon exists.
495
	 * @param constructedHigherTaxon
496
	 * @param state 
497
	 * @return
498
	 */
499
	private Taxon findExistingNonParentTaxon(CentralAfricaFernsImportState state, Taxon constructedHigherTaxon) {
500
		BotanicalName constructedName = CdmBase.deproxy(constructedHigherTaxon.getName(), BotanicalName.class);
501
		String titleCache = constructedName.getTitleCache();
502
		String nameCache = constructedName.getNameCache();
503
		UUID existingUuid = titleCacheTaxonMap.get(titleCache);
504
		if (existingUuid == null){
505
			existingUuid = nameCacheTaxonMap.get(nameCache);
506
		}
507
		Taxon relatedTaxon = null;
508
		if (existingUuid != null){
509
			relatedTaxon = state.getRelatedObject(HIGHER_TAXON_NAMESPACE, nameCache, Taxon.class);
510
			if (relatedTaxon == null){
511
				//TODO find for partition
512
				relatedTaxon = (Taxon)getTaxonService().find(existingUuid);
513
				if (relatedTaxon == null){
514
					logger.info(state.getTaxonNumber() +  " - Could not find existing name ("+nameCache+") in related objects map");
515
				}else{
516
					state.addRelatedObject(HIGHER_TAXON_NAMESPACE, nameCache, relatedTaxon);
517
				}
518
			}
519
		}
520
		return relatedTaxon;
521
	}
522

    
523
	/**
524
	 * Checks if a taxon is a grand parent of another taxon
525
	 * @param lowerTaxon
526
	 * @param higherTaxon
527
	 * @return
528
	 */
529
	private Taxon checkIsGrandParent(Taxon childTaxon, Taxon grandParentTaxon) {
530
		BotanicalName lowerName = CdmBase.deproxy(childTaxon.getName(), BotanicalName.class);
531
		BotanicalName higherName = CdmBase.deproxy(grandParentTaxon.getName(), BotanicalName.class);
532

    
533
		//TODO was wenn lowerTaxon constructed ist
534
		logger.warn("checkIsGrandParent not yet fully implemented");
535
		Taxon nextParent = getParent(childTaxon); 
536
		if (namesMatch(nextParent, grandParentTaxon)){
537
			//TODO which one to return? Merging ?
538
			logger.warn("checkIsGrandParent(matching) not yet fully implemented");
539
			return grandParentTaxon;
540
		}else{
541
			if (compareRanks(lowerName, higherName) >= 0){
542
				return null;
543
			}else{
544
				return checkIsGrandParent(childTaxon, grandParentTaxon);
545
			}
546
		}
547
	}
548

    
549

    
550
	/**
551
	 * Checks if the name caches match.
552
	 * @param name1
553
	 * @param name2
554
	 * @return
555
	 */
556
	private boolean nameCachesMatch(BotanicalName name1, BotanicalName name2) {
557
		return CdmUtils.nullSafeEqual(name1.getNameCache(), name2.getNameCache());
558
	}
559
	
560
	/**
561
	 * Checks if the name caches of the related names match.
562
	 *@param taxon1
563
	 * @param taxon2
564
	 * @return
565
	 */
566
	private boolean nameCachesMatch(Taxon taxon1, Taxon taxon2) {
567
		BotanicalName name1 = CdmBase.deproxy(taxon1.getName(), BotanicalName.class);
568
		BotanicalName name2 = CdmBase.deproxy(taxon2.getName(), BotanicalName.class);
569
		return nameCachesMatch(name1, name2);
570
	}
571

    
572

    
573
	/**
574
	 * Checks if all authors match
575
	 * @param name1
576
	 * @param name2
577
	 * @return
578
	 */
579
	private boolean authorsMatch(NonViralName<?> name1, NonViralName<?> name2) {
580
		String combinationAuthor1 = name1.computeCombinationAuthorNomenclaturalTitle();
581
		String combinationAuthor2 = name2.computeCombinationAuthorNomenclaturalTitle();
582
		String basionymAuthor1 = name1.computeBasionymAuthorNomenclaturalTitle();
583
		String basionymAuthor2 = name2.computeBasionymAuthorNomenclaturalTitle();
584
		String exCombinationAuthor1 = name1.computeExCombinationAuthorNomenclaturalTitle();
585
		String exCombinationAuthor2 = name2.computeExCombinationAuthorNomenclaturalTitle();
586
		String exBasionymAuthor1 = name1.computeExBasionymAuthorNomenclaturalTitle();
587
		String exBasionymAuthor2 = name2.computeExBasionymAuthorNomenclaturalTitle();
588
		boolean result = 
589
			CdmUtils.nullSafeEqual(combinationAuthor1, combinationAuthor2) &&
590
			CdmUtils.nullSafeEqual(basionymAuthor1, basionymAuthor2) &&
591
			CdmUtils.nullSafeEqual(exCombinationAuthor1, exCombinationAuthor2) &&
592
			CdmUtils.nullSafeEqual(exBasionymAuthor1, exBasionymAuthor2);
593
		return result;
594
	}
595
	
596
	/**
597
	 * Checks if all authors of the related names match.
598
	 * @param taxon1
599
	 * @param taxon2
600
	 * @return
601
	 */
602
	private boolean authorsMatch(Taxon taxon1, Taxon taxon2) {
603
		BotanicalName name1 = CdmBase.deproxy(taxon1.getName(), BotanicalName.class);
604
		BotanicalName name2 = CdmBase.deproxy(taxon2.getName(), BotanicalName.class);
605
		return authorsMatch(name1, name2);
606
	}
607

    
608
	/**
609
	 * Compares ranks of 2 names.
610
	 * @param parentName
611
	 * @param constructedName
612
	 * @return
613
	 */
614
	private int compareRanks(BotanicalName name1, BotanicalName name2) {
615
		return name1.getRank().compareTo(name2.getRank());
616
	}
617
	
618
	/**
619
	 * Compares the ranks of the according names.
620
	 * @param taxon1
621
	 * @param taxon2
622
	 * @return
623
	 */
624
	private int compareRanks(Taxon taxon1, Taxon taxon2) {
625
		BotanicalName name1 = CdmBase.deproxy(taxon1.getName(), BotanicalName.class);
626
		BotanicalName name2 = CdmBase.deproxy(taxon2.getName(), BotanicalName.class);
627
		return compareRanks(name1, name2);
628
	}
629
	
630
	
631

    
632
	/**
633
	 * Checks if 2 names match.
634
	 * Current strategy: true, if ranks are equal, nameCaches match and authors match 
635
	 * @param name1
636
	 * @param name2
637
	 * @return
638
	 */
639
	private boolean namesMatch(BotanicalName name1, BotanicalName name2) {
640
		return compareRanks(name1, name2)==0 && nameCachesMatch(name1, name2) && authorsMatch(name1, name2);
641
	}
642
	
643
	/**
644
	 * Checks if the according names match.
645
	 * @see #namesMatch(BotanicalName, BotanicalName)
646
	 * @param taxon1
647
	 * @param taxon2
648
	 * @return
649
	 */
650
	private boolean namesMatch(Taxon taxon1, Taxon taxon2) {
651
		BotanicalName name1 = CdmBase.deproxy(taxon1.getName(), BotanicalName.class);
652
		BotanicalName name2 = CdmBase.deproxy(taxon2.getName(), BotanicalName.class);
653
		return namesMatch(name1, name2);
654
	}
655

    
656
	
657
	/**
658
	 * Returns the only parent of the taxon. If not parent exists <code>null</code> is
659
	 * returned.
660
	 * TODO move to taxon class (with classification)
661
	 * @param child
662
	 * @return
663
	 * @throws IllegalStateException if taxon belongs to multiple states
664
	 */
665
	private Taxon getParent(Taxon child) {
666
		int countNodes = child.getTaxonNodes().size();
667
		if (countNodes < 1){
668
			return null;
669
		}else if (countNodes > 1){
670
			throw new IllegalStateException("Multiple nodes exist for child taxon. This is an invalid state for this import.");
671
		}else{
672
			TaxonNode childNode = child.getTaxonNodes().iterator().next();
673
			TaxonNode parentNode = childNode.getParent();
674
			if (parentNode != null){
675
				return parentNode.getTaxon();
676
			}else{
677
				return null;
678
			}
679
		}
680
	}
681
	
682

    
683
	/**
684
	 * Persists and saves the newly created taxon to the CDM store and to the look-up
685
	 * maps.
686
	 * @param state
687
	 * @param constructedHigherTaxon
688
	 * @return
689
	 */
690
	private Taxon saveConstructedTaxon(CentralAfricaFernsImportState state, Taxon constructedHigherTaxon) {
691
		BotanicalName constructedName = CdmBase.deproxy(constructedHigherTaxon.getName(), BotanicalName.class);
692
		String nameCache = constructedName.getNameCache();
693
		String titleCache = constructedName.getTitleCache();
694
		nameCacheTaxonMap.put(nameCache, constructedHigherTaxon.getUuid());
695
		titleCacheTaxonMap.put(titleCache, constructedHigherTaxon.getUuid());
696
		state.addRelatedObject(HIGHER_TAXON_NAMESPACE, nameCache, constructedHigherTaxon);
697
		
698
		//persist
699
//		Reference citation = state.getConfig().getSourceReference(); //throws nonUniqueObject exception
700
		Reference<?> citation = null;  
701
		String id = state.getTaxonNumber() + "-" + constructedName.getRank().getTitleCache();
702
		addOriginalSource(constructedName, id, NAME_NAMESPACE, citation);
703
		addOriginalSource(constructedHigherTaxon, id, TAXON_NAMESPACE, citation);
704
		getTaxonService().save(constructedHigherTaxon);
705
		
706
		return constructedHigherTaxon;
707
	}
708

    
709

    
710
	//TODO use Mapper
711
	/**
712
	 * Adds the parent child relationship. Creates and saves the classification if needed.
713
	 * Adds parent and child to the classification.
714
	 * @param state
715
	 * @param treeRefFk
716
	 * @param child
717
	 * @param parent
718
	 * @param citation
719
	 * @param microCitation
720
	 * @return
721
	 */
722
	private boolean makeTaxonomicallyIncluded(CentralAfricaFernsImportState state, Integer treeRefFk, Taxon child, Taxon parent, Reference citation, String microCitation){
723
		String treeKey;
724
		UUID treeUuid;
725
		if (treeRefFk == null){
726
			treeKey = "1";  // there is only one tree and it gets the map key '1'
727
			treeUuid = state.getConfig().getClassificationUuid();
728
		}else{
729
			treeKey =String.valueOf(treeRefFk);
730
			treeUuid = state.getTreeUuidByTreeKey(treeKey);
731
		}
732
		Classification tree = (Classification)state.getRelatedObject(DbImportTaxIncludedInMapper.TAXONOMIC_TREE_NAMESPACE, treeKey);
733
		if (tree == null){
734
			IClassificationService service = state.getCurrentIO().getClassificationService();
735
			tree = service.find(treeUuid);
736
			if (tree == null){
737
				String treeName = state.getConfig().getClassificationName();
738
				tree = Classification.NewInstance(treeName);
739
				tree.setUuid(treeUuid);
740
				//FIXME tree reference
741
				tree.setReference(citation);
742
				service.save(tree);
743
			}
744
			state.addRelatedObject(DbImportTaxIncludedInMapper.TAXONOMIC_TREE_NAMESPACE, treeKey, tree);
745
		}
746
		
747
		TaxonNode childNode;
748
		if (parent != null){
749
			childNode = tree.addParentChild(parent, child, citation, microCitation);
750
		}else{
751
			childNode = tree.addChildTaxon(child, citation, microCitation);
752
		}
753
		return (childNode != null);
754
	}
755

    
756

    
757
	/**
758
	 * Reasons if a higher taxon should exist. If it should exist it returns it as a new taxon.
759
	 * Returns null otherwise.
760
	 * @return
761
	 * @throws SQLException
762
	 */
763
	private Taxon constructNextHigherTaxon(CentralAfricaFernsImportState state, ResultSet rs, Taxon childTaxon, Epithets epithets) throws SQLException {
764
		
765
		Taxon result = null;
766
		BotanicalName childName = CdmBase.deproxy(childTaxon.getName(), BotanicalName.class);
767
		Rank childRank = childName.getRank();
768
		BotanicalName higherName;
769
		higherName = handleInfraSpecific(childRank, epithets);
770
		if (higherName.getRank() == null){
771
			handleSpecies(childRank, higherName,  epithets);
772
		}
773
		if (higherName.getRank() == null){
774
			handleInfraGeneric(childRank, higherName, epithets);
775
		}
776
		if (higherName.getRank() == null){
777
			handleUninomial(childRank, higherName, epithets);
778
		}
779
		
780
		if (higherName.getRank() != null){
781
			result = Taxon.NewInstance(higherName, childTaxon.getSec());
782
			//TODO correct??
783
			setAuthor(higherName, rs, state.getTaxonNumber(), true);
784
//			UUID uuid = higherName.getUuid();
785
//			String name = higherName.getNameCache();
786
//			taxonMap.put(name, uuid);
787
//			state.addRelatedObject(HIGHER_TAXON_NAMESPACE, higherName.getNameCache(), result);
788
		}
789
		return result;
790
	}
791

    
792
	private BotanicalName handleInfraSpecific(Rank lowerTaxonRank, Epithets epithets) {
793

    
794
		BotanicalName taxonName = BotanicalName.NewInstance(null);
795
		Rank newRank = null;
796
		
797
		if (StringUtils.isNotBlank(epithets.subFormaName)   && lowerTaxonRank.isLower(Rank.SUBFORM())){
798
			taxonName.setInfraSpecificEpithet(epithets.subFormaName);
799
			newRank =  Rank.SUBFORM();
800
		}else if (StringUtils.isNotBlank(epithets.formaName)  && lowerTaxonRank.isLower(Rank.FORM())){
801
			taxonName.setInfraSpecificEpithet(epithets.formaName);
802
			newRank =  Rank.FORM();
803
		}else if (StringUtils.isNotBlank(epithets.subVariety)  && lowerTaxonRank.isLower(Rank.SUBVARIETY())){
804
			taxonName.setInfraSpecificEpithet(epithets.subVariety);
805
			newRank =  Rank.SUBVARIETY();
806
		}else if (StringUtils.isNotBlank(epithets.varietyName)  && lowerTaxonRank.isLower(Rank.VARIETY())){
807
			taxonName.setInfraSpecificEpithet(epithets.varietyName);
808
			newRank =  Rank.VARIETY();
809
		}else if (StringUtils.isNotBlank(epithets.subspeciesName)  && lowerTaxonRank.isLower(Rank.SUBSPECIES())){
810
			taxonName.setInfraSpecificEpithet(epithets.subspeciesName);
811
			newRank = Rank.SUBSPECIES();
812
		}
813
		
814
		if (newRank != null){
815
			taxonName.setSpecificEpithet(epithets.specificEpithet);
816
			taxonName.setGenusOrUninomial(epithets.genusName);
817
			taxonName.setRank(newRank);
818
		}
819
		
820
		return taxonName;
821
	}
822

    
823
	private BotanicalName handleSpecies(Rank lowerTaxonRank, BotanicalName taxonName, Epithets epithets) {
824
		Rank newRank = null;
825
		
826
		if (StringUtils.isNotBlank(epithets.specificEpithet)  && lowerTaxonRank.isLower(Rank.SPECIES())){
827
			taxonName.setSpecificEpithet(epithets.specificEpithet);
828
			newRank = Rank.SPECIES();
829
		}
830
		if (newRank != null){
831
			taxonName.setGenusOrUninomial(epithets.genusName);
832
			taxonName.setRank(newRank);
833
		}
834
		return taxonName;
835
	}
836

    
837
	private BotanicalName handleInfraGeneric(Rank lowerTaxonRank, BotanicalName taxonName, Epithets epithets) {
838
		Rank newRank = null;
839
		
840
		if (StringUtils.isNotBlank(epithets.seriesName)  && lowerTaxonRank.isLower(Rank.SERIES())){
841
			taxonName.setInfraGenericEpithet(epithets.seriesName);
842
			newRank = Rank.SERIES();
843
		}else if (StringUtils.isNotBlank(epithets.subsectionName)  && lowerTaxonRank.isLower(Rank.SUBSECTION_BOTANY())){
844
			taxonName.setInfraGenericEpithet(epithets.subsectionName);
845
			newRank =  Rank.SUBSECTION_BOTANY();
846
		}else if (StringUtils.isNotBlank(epithets.sectionName)  && lowerTaxonRank.isLower(Rank.SECTION_BOTANY())){
847
			taxonName.setInfraGenericEpithet(epithets.sectionName);
848
			newRank =  Rank.SECTION_BOTANY();
849
		}else if (StringUtils.isNotBlank(epithets.subGenusName) && lowerTaxonRank.isLower(Rank.SUBGENUS())){
850
			taxonName.setInfraGenericEpithet(epithets.subGenusName);
851
			newRank = Rank.SUBGENUS();
852
		}
853
		if (newRank != null){
854
			taxonName.setGenusOrUninomial(epithets.genusName);
855
			taxonName.setRank(newRank);
856
		}
857
		return taxonName;
858
	}
859

    
860

    
861

    
862
	private BotanicalName handleUninomial(Rank lowerTaxonRank, BotanicalName taxonName,  Epithets epithets) {
863
		
864
		Rank newRank = null;
865
		if (StringUtils.isNotBlank(epithets.genusName) && lowerTaxonRank.isLower(Rank.GENUS())){
866
			taxonName.setGenusOrUninomial(epithets.genusName);
867
			newRank =  Rank.GENUS();
868
		}else if (StringUtils.isNotBlank(epithets.subTribusName) && lowerTaxonRank.isLower(Rank.SUBTRIBE())){
869
			taxonName.setGenusOrUninomial(epithets.subTribusName);
870
			newRank =  Rank.SUBTRIBE();
871
		}else if (StringUtils.isNotBlank(epithets.tribusName) && lowerTaxonRank.isLower(Rank.TRIBE())){
872
			taxonName.setGenusOrUninomial(epithets.tribusName);
873
			newRank =  Rank.TRIBE();
874
		}else if (StringUtils.isNotBlank(epithets.subFamilyName) && lowerTaxonRank.isLower(Rank.SUBFAMILY())){
875
			taxonName.setGenusOrUninomial(epithets.subFamilyName);
876
			newRank =  Rank.SUBFAMILY();
877
		}else if (StringUtils.isNotBlank(epithets.familyName) && lowerTaxonRank.isLower(Rank.FAMILY())){
878
			taxonName.setGenusOrUninomial(epithets.familyName);
879
			newRank =  Rank.FAMILY();
880
		}else if (StringUtils.isNotBlank(epithets.subOrderName) && lowerTaxonRank.isLower(Rank.SUBORDER())){
881
			taxonName.setGenusOrUninomial(epithets.subOrderName);
882
			newRank =  Rank.SUBORDER();
883
		}else if (StringUtils.isNotBlank(epithets.orderName) && lowerTaxonRank.isLower(Rank.ORDER())){
884
			taxonName.setGenusOrUninomial(epithets.orderName);
885
			newRank =  Rank.ORDER();
886
		}
887
		taxonName.setRank(newRank);
888
		return taxonName;
889
	}
890
	
891

    
892
	/**
893
	 * for internal use only, used by MethodMapper
894
	 */
895
	private TaxonBase mapCommonName(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException{
896
		String taxonNumber = state.getTaxonNumber();
897
		String commonNames = rs.getString("Common names");
898
		TaxonBase<?> taxonBase = state.getRelatedObject(state.CURRENT_OBJECT_NAMESPACE, state.CURRENT_OBJECT_ID, TaxonBase.class);
899
		if (StringUtils.isNotBlank(commonNames)){
900
			Taxon taxon = getAcceptedTaxon(taxonBase);
901
			if ( taxon != null ){
902
				TaxonDescription description = getTaxonDescription(taxon, false, true);
903
				String[] split = commonNames.split(",");
904
				for (String commonNameString: split){
905
					CommonTaxonName commonName = CommonTaxonName.NewInstance(commonNameString.trim(), Language.ENGLISH());
906
					description.addElement(commonName);				
907
				}
908
			}else{
909
				logger.warn(taxonNumber + " - Accepted taxon for synonym can't be defined for common name. Synonym " + taxonBase.getName().getTitleCache());
910
			}
911
		}
912
		return taxonBase;
913
	}
914
	
915

    
916
	/**
917
	 * for internal use only, used by MethodMapper
918
	 */
919
	private TaxonBase mapDistribution(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException{
920
		try {
921
			String taxonNumber = state.getTaxonNumber();
922
//			logger.info(taxonNumber);
923
			TaxonBase<?> taxonBase = state.getRelatedObject(state.CURRENT_OBJECT_NAMESPACE, state.CURRENT_OBJECT_ID, TaxonBase.class);
924
			String countriesString = rs.getString("Distribution - Country");
925
			String province = rs.getString("Distribution - Province");
926
			String distributionDetailed = rs.getString("Distribution - detailed");
927
			if (taxonBase != null){
928
				TaxonNameBase<?,?> nameUsedInSource = taxonBase.getName();
929
				Taxon taxon = getAcceptedTaxon(taxonBase);
930
				if (taxon != null){
931
				
932
					if (StringUtils.isNotBlank(countriesString) ){
933
						makeCountries(state, taxonNumber, taxon, nameUsedInSource, countriesString, province, distributionDetailed);
934
					}
935
					makeProvince(taxon, province);
936
					makeDistributionDetailed(taxon, distributionDetailed);
937
				}else{
938
					logger.warn(taxonNumber + " - Accepted taxon for synonym can't be defined for distribution. Synonym " + taxonBase.getName().getTitleCache());
939
				}
940
			}else{
941
				logger.warn(" - " + taxonNumber + ": TaxonBase was null");
942
			}
943
			return taxonBase;
944
		} catch (Exception e) {
945
			e.printStackTrace();
946
			return null;
947
		}
948
	}
949
	
950
	
951
	/**
952
	 * for internal use only, used by MethodMapper
953
	 * @param commonNames 
954
	 */
955
	private TaxonBase mapEcology(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException{
956
		String taxonNumber = state.getTaxonNumber();
957
		String ecologyString = rs.getString("Ecology");
958
		TaxonBase<?> taxonBase = state.getRelatedObject(state.CURRENT_OBJECT_NAMESPACE, state.CURRENT_OBJECT_ID, TaxonBase.class);
959
		if (StringUtils.isNotBlank(ecologyString)){
960
			Taxon taxon = getAcceptedTaxon(taxonBase);
961
			
962
			if (taxon != null){
963
				TaxonDescription description = getTaxonDescription(taxon, false, true);
964
				TextData ecology = TextData.NewInstance(Feature.ECOLOGY());
965
				ecology.putText(Language.ENGLISH(), ecologyString.trim());
966
				description.addElement(ecology);				
967
			}else{
968
				logger.warn(taxonNumber + " - Accepted taxon for synonym can't be defined for ecology. Synonym " + taxonBase.getName().getTitleCache());
969
			}
970
		}
971
		return taxonBase;
972
	}
973

    
974

    
975

    
976

    
977
	private void makeDistributionDetailed(Taxon taxon, String distributionDetailed) {
978
		if (StringUtils.isNotBlank(distributionDetailed)){
979
			TaxonDescription description = getTaxonDescription(taxon, false, true);
980
			TextData distribution = TextData.NewInstance(Feature.DISTRIBUTION());
981
			description.addElement(distribution);
982
			distribution.putText(Language.ENGLISH(), distributionDetailed);
983
		}
984
	}
985

    
986
	
987
	private void makeProvince(Taxon taxon, String province) {
988
		if (StringUtils.isNotBlank(province)){
989
			TaxonDescription description = getTaxonDescription(taxon, false, true);
990
			TextData distribution = TextData.NewInstance(Feature.DISTRIBUTION());
991
			description.addElement(distribution);
992
			distribution.putText(Language.ENGLISH(), province);
993
		}
994
	}
995
	
996

    
997
	/**
998
	 * @param state
999
	 * @param taxonNumber
1000
	 * @param taxonBase
1001
	 * @param countriesString
1002
	 */
1003
	private void makeCountries(CentralAfricaFernsImportState state, String taxonNumber, Taxon taxon, TaxonNameBase nameUsedInSource, String countriesString, String province, String distributionDetailed) {
1004
		countriesString = countriesString.replaceAll("\\*", "");  
1005
		countriesString = countriesString.replace("  ", " ");
1006
		countriesString = countriesString.replace(", endemic", " - endemic");
1007
		countriesString = countriesString.replace("(endemic)", " - endemic");
1008
		countriesString = countriesString.replace("(introduced)", " - introduced");
1009
		countriesString = countriesString.replace("(naturalised)", " - naturalised");
1010
		countriesString = countriesString.replace("Madagascar-", "Madagascar -");
1011
		countriesString = countriesString.replace("Mah\u00e9", "Mahe");
1012
		 
1013
		String[] split = countriesString.split("[,;]");
1014
		String remainingString = null;
1015
		for (String countryString : split){
1016
			countryString = CdmUtils.concat(", ", remainingString , countryString);
1017
			if (countryString.matches(".*\\(.*") && ! countryString.matches(".*\\).*")){
1018
				remainingString = countryString;
1019
				continue;
1020
			}
1021
			remainingString = null;
1022
			try {
1023
				makeSingleCountry(state, taxonNumber, taxon, nameUsedInSource, countryString.trim());
1024
			} catch (UndefinedTransformerMethodException e) {
1025
				e.printStackTrace();
1026
			}
1027
		}
1028
	}
1029

    
1030

    
1031
	private void makeSingleCountry(CentralAfricaFernsImportState state, String taxonNumber, Taxon taxon, TaxonNameBase nameUsedInSource, String country) throws UndefinedTransformerMethodException {
1032
		boolean areaDoubtful = false;
1033
		Distribution distribution = Distribution.NewInstance(null, PresenceTerm.PRESENT());
1034
		Reference<?> sourceReference = this.sourceReference;
1035
		distribution.addSource(OriginalSourceType.Import, taxonNumber, "Distribution_Country", sourceReference, null, nameUsedInSource, null);
1036
		NamedArea area = null;
1037
		//empty
1038
		if (StringUtils.isBlank(country)){
1039
			return;
1040
		}
1041
		country = country.trim();
1042
		//doubtful
1043
		if (country.startsWith("?")){
1044
			areaDoubtful = true;
1045
			country = country.substring(1).trim();
1046
		}
1047
		//status
1048
		country = makeCountryStatus(state, country, distribution);
1049
		
1050
		//brackets
1051
		country = makeCountryBrackets(state, taxonNumber, taxon, nameUsedInSource, country);
1052
		String countryWithoutIslands = null;
1053
		String countryWithoutDot = null;
1054
		if (country.endsWith(" Isl.") || country.endsWith(" isl.") ){
1055
			countryWithoutIslands = country.substring(0, country.length()-5);
1056
		}
1057
		if (country.endsWith(".")){
1058
			countryWithoutDot = country.substring(0, country.length()-1);
1059
		}
1060
		if (country.endsWith("*")){
1061
			country = country.substring(0, country.length()-1);
1062
		}
1063
		if (country.endsWith("Islands")){
1064
			country = country.replace("Islands", "Is.");
1065
		}
1066
		
1067
		
1068
		//areas
1069
		if (TdwgAreaProvider.isTdwgAreaLabel(country)){
1070
			//tdwg
1071
			area = TdwgAreaProvider.getAreaByTdwgLabel(country);
1072
		}else if (TdwgAreaProvider.isTdwgAreaLabel(countryWithoutIslands)){
1073
			//tdwg
1074
			area = TdwgAreaProvider.getAreaByTdwgLabel(countryWithoutIslands);
1075
		}else if (TdwgAreaProvider.isTdwgAreaLabel(countryWithoutDot)){
1076
			//tdwg
1077
			area = TdwgAreaProvider.getAreaByTdwgLabel(countryWithoutDot);
1078
		}else if ( (area = state.getTransformer().getNamedAreaByKey(country)) != null) {
1079
			//area already set
1080
		}else if (Country.isCountryLabel(country)){
1081
			//iso
1082
			area = Country.getCountryByLabel(country);
1083
		}else{
1084
			//others
1085
			NamedAreaLevel level = null;
1086
			NamedAreaType areaType = null;
1087
			
1088
			UUID uuid = state.getTransformer().getNamedAreaUuid(country);
1089
			if (uuid == null){
1090
				logger.error(taxonNumber + " - Unknown country: " + country);
1091
			}
1092
			area = getNamedArea(state, uuid, country, country, country, areaType, level);
1093
		}
1094
		
1095
		distribution.setArea(area);
1096
		if (areaDoubtful == true){
1097
			if (distribution.getStatus().equals(PresenceTerm.PRESENT())){
1098
				distribution.setStatus(PresenceTerm.PRESENT_DOUBTFULLY());
1099
			}
1100
		}
1101
		TaxonDescription description = getTaxonDescription(taxon, false, true);
1102
		description.addElement(distribution);
1103
	}
1104

    
1105

    
1106

    
1107
	/**
1108
	 * @param state
1109
	 * @return
1110
	 */
1111
	private Reference<?> sourceReference = null;
1112
	private Reference<?> getFernsSourceReference(CentralAfricaFernsImportState state) {
1113
//		if (sourceReference == null || true){
1114
			Reference<?> tmpReference = state.getConfig().getSourceReference();
1115
			sourceReference = getReferenceService().find(tmpReference.getUuid());
1116
//		}
1117
		return sourceReference;
1118
	}
1119

    
1120

    
1121
	private String makeCountryBrackets(CentralAfricaFernsImportState state, String taxonNumber, Taxon taxon, TaxonNameBase<?,?> nameUsedInSource, String country) {
1122
		String[] split = (country + " ").split("\\(.*\\)");
1123
		if (split.length == 2){
1124
			String bracket = country.substring(split[0].length()+1, country.indexOf(")"));
1125
			country = split[0].trim();
1126
			makeCountries(state, taxonNumber, taxon, nameUsedInSource, bracket, null, null);
1127
		}else if (split.length ==1){
1128
			//do nothing
1129
		}else{
1130
			logger.warn("Illegal length");
1131
		}
1132
		return country;
1133
	}
1134

    
1135
	private String makeCountryStatus(CentralAfricaFernsImportState state, String country, Distribution distribution) throws UndefinedTransformerMethodException {
1136
		PresenceAbsenceTermBase<?> status = null;
1137
		String[] split = country.split(" - ");
1138
		
1139
		if (split.length == 2){
1140
			country = split[0].trim();
1141
			String statusString = split[1];
1142
			statusString = statusString.replace(".", "");
1143
			status = state.getTransformer().getPresenceTermByKey(statusString);
1144
			if (status == null){
1145
				logger.warn("No status found: "+  statusString);
1146
			}
1147
//			UUID uuid = null;
1148
//			status = getPresenceTerm(state, uuid, statusString, statusString, null);
1149
		}else if (split.length == 1){
1150
			//nothing to do
1151
		}else{
1152
			logger.warn("Invalid length: " + split.length);
1153
		}
1154
		if (status != null){
1155
			distribution.setStatus(status);
1156
		}
1157
		return country;
1158
	}
1159

    
1160

    
1161
	
1162

    
1163
	/* (non-Javadoc)
1164
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
1165
	 */
1166
	@Override
1167
	protected boolean doCheck(CentralAfricaFernsImportState state){
1168
		IOValidator<CentralAfricaFernsImportState> validator = new CentralAfricaFernsTaxonImportValidator();
1169
		return validator.validate(state);
1170
	}
1171

    
1172
	/* (non-Javadoc)
1173
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
1174
	 */
1175
	@Override
1176
	protected boolean isIgnore(CentralAfricaFernsImportState state){
1177
		return ! state.getConfig().isDoRelTaxa();
1178
	}
1179

    
1180
	
1181
	
1182
//************************ OLD **********************************************************
1183

    
1184
	/**
1185
	 * Adds the higherTaxon authors to the existingHigherTaxon authors if the higherTaxon has authors and 
1186
	 * the existingHigherTaxon has no authors.
1187
	 * Returns false if both taxa have authors and the authors differ from each other.
1188
	 * @param higherTaxon
1189
	 * @param existingHigherTaxon
1190
	 */
1191
	private boolean mergeAuthors_old(Taxon higherTaxon, Taxon existingHigherTaxon) {
1192
		NonViralName<?> existingName = CdmBase.deproxy(higherTaxon.getName(), NonViralName.class);
1193
		NonViralName<?> newName = CdmBase.deproxy(existingHigherTaxon.getName(), NonViralName.class);
1194
		if (existingName == newName){
1195
			return true;
1196
		}
1197
		if (! newName.hasAuthors()){
1198
			return true;
1199
		}
1200
		if (! existingName.hasAuthors()){
1201
			existingName.setCombinationAuthorTeam(newName.getCombinationAuthorTeam());
1202
			existingName.setExCombinationAuthorTeam(newName.getExCombinationAuthorTeam());
1203
			existingName.setBasionymAuthorTeam(newName.getBasionymAuthorTeam());
1204
			existingName.setExBasionymAuthorTeam(newName.getExBasionymAuthorTeam());
1205
			return true;
1206
		}
1207
		boolean authorsAreSame = true;
1208
		authorsAreSame &= getNomTitleNz(existingName.getCombinationAuthorTeam()).equals(getNomTitleNz(newName.getCombinationAuthorTeam()));
1209
		authorsAreSame &= getNomTitleNz(existingName.getExCombinationAuthorTeam()).equals(getNomTitleNz(newName.getExCombinationAuthorTeam()));
1210
		authorsAreSame &= getNomTitleNz(existingName.getBasionymAuthorTeam()).equals(getNomTitleNz(newName.getBasionymAuthorTeam()));
1211
		authorsAreSame &= getNomTitleNz(existingName.getExBasionymAuthorTeam()).equals(getNomTitleNz(newName.getExBasionymAuthorTeam()));
1212
		return authorsAreSame;
1213
		
1214
		
1215
	}
1216

    
1217
	/**
1218
	 * Returns the nomenclatural title of the author. Returns empty string if author is <code>null</code> or
1219
	 * titleCache is <code>null</code>.
1220
	 * @param author
1221
	 * @return
1222
	 */
1223
	private String getNomTitleNz(INomenclaturalAuthor author) {
1224
		if (author != null){
1225
			return CdmUtils.Nz(author.getNomenclaturalTitle());
1226
		}else{
1227
			return "";
1228
		}
1229
	}
1230

    
1231
	private Taxon getExistingHigherTaxon_old(Taxon child, Taxon higherTaxon) {
1232
		int countNodes = child.getTaxonNodes().size();
1233
		if (countNodes < 1){
1234
			return null;
1235
		}else if (countNodes > 1){
1236
			throw new IllegalStateException("Multiple nodes exist for child taxon. This is an invalid state.");
1237
		}else{
1238
			TaxonNode childNode = child.getTaxonNodes().iterator().next();
1239
			TaxonNode parentNode = childNode.getParent();
1240
			if (parentNode != null){
1241
				String existingParentTitle = parentNode.getTaxon().getName().getTitleCache();
1242
				String newParentTitle = higherTaxon.getName().getTitleCache();
1243
				if (existingParentTitle.equals(newParentTitle)){
1244
					return parentNode.getTaxon();
1245
				}
1246
			}
1247
			return null;
1248
		}
1249
	}
1250

    
1251

    
1252

    
1253
	/**
1254
	 * Tests if this the child taxon already is a child of the higher taxon.
1255
	 * @param child
1256
	 * @param higherTaxon
1257
	 * @return
1258
	 */
1259
	private boolean includedRelationshipExists_Old(Taxon child, Taxon higherTaxon) {
1260
		int countNodes = higherTaxon.getTaxonNodes().size();
1261
		if (countNodes < 1){
1262
			return false;
1263
		}else if (countNodes > 1){
1264
			throw new IllegalStateException("Multiple nodes exist for higher taxon. This is an invalid state.");
1265
		}else{
1266
			TaxonNode higherNode = higherTaxon.getTaxonNodes().iterator().next();
1267
			return childExists_old(child, higherNode);
1268
		}
1269
	}
1270

    
1271

    
1272

    
1273
	private boolean childExists_old(Taxon child, TaxonNode higherNode) {
1274
		for (TaxonNode childNode : higherNode.getChildNodes()){
1275
			String existingChildTitle = childNode.getTaxon().getName().getTitleCache();
1276
			String newChildTitle = child.getName().getTitleCache();
1277
			if (existingChildTitle.equals(newChildTitle)){
1278
				return true;
1279
			}
1280
		}
1281
		return false;
1282
	}
1283

    
1284

    
1285
}
(6-6/7)