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.mapping.DbImportMapping;
30
import eu.etaxonomy.cdm.io.common.mapping.DbImportMethodMapper;
31
import eu.etaxonomy.cdm.io.common.mapping.DbImportTaxIncludedInMapper;
32
import eu.etaxonomy.cdm.io.common.mapping.IMappingImport;
33
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
34
import eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns.validation.CentralAfricaFernsTaxonImportValidator;
35
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
36
import eu.etaxonomy.cdm.model.common.CdmBase;
37
import eu.etaxonomy.cdm.model.common.Language;
38
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
39
import eu.etaxonomy.cdm.model.description.Distribution;
40
import eu.etaxonomy.cdm.model.description.Feature;
41
import eu.etaxonomy.cdm.model.description.PresenceTerm;
42
import eu.etaxonomy.cdm.model.description.TaxonDescription;
43
import eu.etaxonomy.cdm.model.description.TextData;
44
import eu.etaxonomy.cdm.model.location.NamedArea;
45
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
46
import eu.etaxonomy.cdm.model.location.NamedAreaType;
47
import eu.etaxonomy.cdm.model.location.TdwgArea;
48
import eu.etaxonomy.cdm.model.location.WaterbodyOrCountry;
49
import eu.etaxonomy.cdm.model.name.BotanicalName;
50
import eu.etaxonomy.cdm.model.name.NonViralName;
51
import eu.etaxonomy.cdm.model.name.Rank;
52
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
53
import eu.etaxonomy.cdm.model.reference.Reference;
54
import eu.etaxonomy.cdm.model.taxon.Classification;
55
import eu.etaxonomy.cdm.model.taxon.Synonym;
56
import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
57
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
58
import eu.etaxonomy.cdm.model.taxon.Taxon;
59
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
60
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
61

    
62

    
63
/**
64
 * @author a.mueller
65
 */
66

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

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

    
81
	private CentralAfricaFernsImportState state;
82

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

    
90
	/* (non-Javadoc)
91
	 * @see eu.etaxonomy.cdm.io.erms.ErmsImportBase#getIdQuery()
92
	 */
93
	@Override
94
	protected String getIdQuery() {
95
		String strQuery = " SELECT [Taxon number] FROM " + dbTableName;;
96
		return strQuery;
97
	}
98

    
99

    
100
	/* (non-Javadoc)
101
	 * @see eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns.CentralAfricaFernsImportBase#getMapping()
102
	 */
103
	@Override
104
	protected DbImportMapping getMapping() {
105
		if (mapping == null){
106
			mapping = new DbImportMapping();
107
			
108
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "createObject", ResultSet.class, CentralAfricaFernsImportState.class));
109
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapCommonName", ResultSet.class, CentralAfricaFernsImportState.class));
110
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapDistribution", ResultSet.class, CentralAfricaFernsImportState.class ));
111
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapEcology", ResultSet.class, CentralAfricaFernsImportState.class));
112

    
113
		}
114
		return mapping;
115
	}
116

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

    
131
	/* (non-Javadoc)
132
	 * @see eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns.CentralAfricaFernsImportBase#doInvoke(eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns.CentralAfricaFernsImportState)
133
	 */
134
	@Override
135
	protected void doInvoke(CentralAfricaFernsImportState state) {
136
		this.state = state;
137
		fillTaxonMap();
138
		super.doInvoke(state);
139
		return;
140
	}
141

    
142

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

    
162
	/* (non-Javadoc)
163
	 * @see eu.etaxonomy.cdm.io.common.IPartitionedIO#getRelatedObjectsForPartition(java.sql.ResultSet)
164
	 */
165
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs) {
166
		String nameSpace;
167
		Class cdmClass;
168
		Set<String> idSet;
169
		Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<Object, Map<String, ? extends CdmBase>>();
170
		
171
		try{
172
			Set<String> taxonIdSet = new HashSet<String>();
173
//				Set<String> referenceIdSet = new HashSet<String>();
174
			while (rs.next()){
175
				handleForeignKey(rs, taxonIdSet, "Current");
176
				handleForeignKey(rs, taxonIdSet, "Taxon number");
177
//				handleForeignKey(rs, referenceIdSet, "PTRefFk");
178
			}
179

    
180
			//taxon map
181
			nameSpace = TAXON_NAMESPACE;
182
			cdmClass = TaxonBase.class;
183
			Map<String, TaxonBase> taxonMap = (Map<String, TaxonBase>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, taxonIdSet, nameSpace);
184
			result.put(nameSpace, taxonMap);
185
				
186
				
187
			//reference map
188
			this.sourceReference = getFernsSourceReference(state);
189
//			nameSpace = "Reference";
190
//			cdmClass = Reference.class;
191
//			Map<String, Person> referenceMap = (Map<String, Person>)getCommonService().getSourcedObjectsByIdInSource(Person.class, teamIdSet, nameSpace);
192
//			result.put(Reference.class, referenceMap);
193

    
194
		} catch (SQLException e) {
195
			throw new RuntimeException(e);
196
		}
197
		return result;
198
	}
199
	
200

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

    
225
	}
226

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

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

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

    
328

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

    
340
		Taxon constructedHigherTaxon = constructNextHigherTaxon(state, rs, child, epithets);
341
		Reference<?> citation = null;
342
		String microcitation = null;
343
		
344
		if (constructedHigherTaxon != null){
345
			handleHigherTaxonMustExist(state, rs, child, epithets, constructedHigherTaxon, citation, microcitation);
346
		}else{
347
			//add taxon to tree if not yet added
348
			if (child.getTaxonNodes().size() == 0){
349
				makeTaxonomicallyIncluded(state, null, child, null, citation, microcitation);
350
			}
351
		}
352
	}
353

    
354

    
355

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

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

    
428

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

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

    
468

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

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

    
530
	/**
531
	 * Checks if a taxon is a grand parent of another taxon
532
	 * @param lowerTaxon
533
	 * @param higherTaxon
534
	 * @return
535
	 */
536
	private Taxon checkIsGrandParent(Taxon childTaxon, Taxon grandParentTaxon) {
537
		BotanicalName lowerName = CdmBase.deproxy(childTaxon.getName(), BotanicalName.class);
538
		BotanicalName higherName = CdmBase.deproxy(grandParentTaxon.getName(), BotanicalName.class);
539

    
540
		//TODO was wenn lowerTaxon constructed ist
541
		logger.warn("checkIsGrandParent not yet fully implemented");
542
		Taxon nextParent = getParent(childTaxon); 
543
		if (namesMatch(nextParent, grandParentTaxon)){
544
			//TODO which one to return? Merging ?
545
			logger.warn("checkIsGrandParent(matching) not yet fully implemented");
546
			return grandParentTaxon;
547
		}else{
548
			if (compareRanks(lowerName, higherName) >= 0){
549
				return null;
550
			}else{
551
				return checkIsGrandParent(childTaxon, grandParentTaxon);
552
			}
553
		}
554
	}
555

    
556

    
557
	/**
558
	 * Checks if the name caches match.
559
	 * @param name1
560
	 * @param name2
561
	 * @return
562
	 */
563
	private boolean nameCachesMatch(BotanicalName name1, BotanicalName name2) {
564
		return CdmUtils.nullSafeEqual(name1.getNameCache(), name2.getNameCache());
565
	}
566
	
567
	/**
568
	 * Checks if the name caches of the related names match.
569
	 *@param taxon1
570
	 * @param taxon2
571
	 * @return
572
	 */
573
	private boolean nameCachesMatch(Taxon taxon1, Taxon taxon2) {
574
		BotanicalName name1 = CdmBase.deproxy(taxon1.getName(), BotanicalName.class);
575
		BotanicalName name2 = CdmBase.deproxy(taxon2.getName(), BotanicalName.class);
576
		return nameCachesMatch(name1, name2);
577
	}
578

    
579

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

    
615
	/**
616
	 * Compares ranks of 2 names.
617
	 * @param parentName
618
	 * @param constructedName
619
	 * @return
620
	 */
621
	private int compareRanks(BotanicalName name1, BotanicalName name2) {
622
		return name1.getRank().compareTo(name2.getRank());
623
	}
624
	
625
	/**
626
	 * Compares the ranks of the according names.
627
	 * @param taxon1
628
	 * @param taxon2
629
	 * @return
630
	 */
631
	private int compareRanks(Taxon taxon1, Taxon taxon2) {
632
		BotanicalName name1 = CdmBase.deproxy(taxon1.getName(), BotanicalName.class);
633
		BotanicalName name2 = CdmBase.deproxy(taxon2.getName(), BotanicalName.class);
634
		return compareRanks(name1, name2);
635
	}
636
	
637
	
638

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

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

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

    
716

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

    
763

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

    
799
	private BotanicalName handleInfraSpecific(Rank lowerTaxonRank, Epithets epithets) {
800

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

    
830
	private BotanicalName handleSpecies(Rank lowerTaxonRank, BotanicalName taxonName, Epithets epithets) {
831
		Rank newRank = null;
832
		
833
		if (StringUtils.isNotBlank(epithets.specificEpithet)  && lowerTaxonRank.isLower(Rank.SPECIES())){
834
			taxonName.setSpecificEpithet(epithets.specificEpithet);
835
			newRank = Rank.SPECIES();
836
		}
837
		if (newRank != null){
838
			taxonName.setGenusOrUninomial(epithets.genusName);
839
			taxonName.setRank(newRank);
840
		}
841
		return taxonName;
842
	}
843

    
844
	private BotanicalName handleInfraGeneric(Rank lowerTaxonRank, BotanicalName taxonName, Epithets epithets) {
845
		Rank newRank = null;
846
		
847
		if (StringUtils.isNotBlank(epithets.seriesName)  && lowerTaxonRank.isLower(Rank.SERIES())){
848
			taxonName.setInfraGenericEpithet(epithets.seriesName);
849
			newRank = Rank.SERIES();
850
		}else if (StringUtils.isNotBlank(epithets.subsectionName)  && lowerTaxonRank.isLower(Rank.SUBSECTION_BOTANY())){
851
			taxonName.setInfraGenericEpithet(epithets.subsectionName);
852
			newRank =  Rank.SUBSECTION_BOTANY();
853
		}else if (StringUtils.isNotBlank(epithets.sectionName)  && lowerTaxonRank.isLower(Rank.SECTION_BOTANY())){
854
			taxonName.setInfraGenericEpithet(epithets.sectionName);
855
			newRank =  Rank.SECTION_BOTANY();
856
		}else if (StringUtils.isNotBlank(epithets.subGenusName) && lowerTaxonRank.isLower(Rank.SUBGENUS())){
857
			taxonName.setInfraGenericEpithet(epithets.subGenusName);
858
			newRank = Rank.SUBGENUS();
859
		}
860
		if (newRank != null){
861
			taxonName.setGenusOrUninomial(epithets.genusName);
862
			taxonName.setRank(newRank);
863
		}
864
		return taxonName;
865
	}
866

    
867

    
868

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

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

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

    
981

    
982

    
983

    
984
	private void makeDistributionDetailed(Taxon taxon, String distributionDetailed) {
985
		if (StringUtils.isNotBlank(distributionDetailed)){
986
			TaxonDescription description = getTaxonDescription(taxon, false, true);
987
			TextData distribution = TextData.NewInstance(Feature.DISTRIBUTION());
988
			description.addElement(distribution);
989
			distribution.putText(Language.ENGLISH(), distributionDetailed);
990
		}
991
	}
992

    
993
	
994
	private void makeProvince(Taxon taxon, String province) {
995
		if (StringUtils.isNotBlank(province)){
996
			TaxonDescription description = getTaxonDescription(taxon, false, true);
997
			TextData distribution = TextData.NewInstance(Feature.DISTRIBUTION());
998
			description.addElement(distribution);
999
			distribution.putText(Language.ENGLISH(), province);
1000
		}
1001
	}
1002
	
1003

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

    
1037

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

    
1112

    
1113

    
1114
	/**
1115
	 * @param state
1116
	 * @return
1117
	 */
1118
	Reference sourceReference = null;
1119
	private Reference getFernsSourceReference(CentralAfricaFernsImportState state) {
1120
//		if (sourceReference == null || true){
1121
			Reference tmpReference = state.getConfig().getSourceReference();
1122
			sourceReference = getReferenceService().find(tmpReference.getUuid());
1123
//		}
1124
		return sourceReference;
1125
	}
1126

    
1127

    
1128
	private String makeCountryBrackets(CentralAfricaFernsImportState state, String taxonNumber, Taxon taxon, TaxonNameBase nameUsedInSource, String country) {
1129
		String[] split = (country + " ").split("\\(.*\\)");
1130
		if (split.length == 2){
1131
			String bracket = country.substring(split[0].length()+1, country.indexOf(")"));
1132
			country = split[0].trim();
1133
			makeCountries(state, taxonNumber, taxon, nameUsedInSource, bracket, null, null);
1134
		}else if (split.length ==1){
1135
			//do nothing
1136
		}else{
1137
			logger.warn("Illegal length");
1138
		}
1139
		return country;
1140
	}
1141

    
1142
	private String makeCountryStatus(CentralAfricaFernsImportState state, String country, Distribution distribution) throws UndefinedTransformerMethodException {
1143
		PresenceTerm status = null;
1144
		String[] split = country.split(" - ");
1145
		
1146
		if (split.length == 2){
1147
			country = split[0].trim();
1148
			String statusString = split[1];
1149
			statusString = statusString.replace(".", "");
1150
			status = state.getTransformer().getPresenceTermByKey(statusString);
1151
			if (status == null){
1152
				logger.warn("No status found: "+  statusString);
1153
			}
1154
//			UUID uuid = null;
1155
//			status = getPresenceTerm(state, uuid, statusString, statusString, null);
1156
		}else if (split.length == 1){
1157
			//nothing to do
1158
		}else{
1159
			logger.warn("Invalid length: " + split.length);
1160
		}
1161
		if (status != null){
1162
			distribution.setStatus(status);
1163
		}
1164
		return country;
1165
	}
1166

    
1167

    
1168
	
1169

    
1170
	/* (non-Javadoc)
1171
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#doCheck(eu.etaxonomy.cdm.io.common.IoStateBase)
1172
	 */
1173
	@Override
1174
	protected boolean doCheck(CentralAfricaFernsImportState state){
1175
		IOValidator<CentralAfricaFernsImportState> validator = new CentralAfricaFernsTaxonImportValidator();
1176
		return validator.validate(state);
1177
	}
1178

    
1179
	/* (non-Javadoc)
1180
	 * @see eu.etaxonomy.cdm.io.common.CdmIoBase#isIgnore(eu.etaxonomy.cdm.io.common.IoStateBase)
1181
	 */
1182
	@Override
1183
	protected boolean isIgnore(CentralAfricaFernsImportState state){
1184
		return ! state.getConfig().isDoRelTaxa();
1185
	}
1186

    
1187
	
1188
	
1189
//************************ OLD **********************************************************
1190

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

    
1224
	/**
1225
	 * Returns the nomenclatural title of the author. Returns empty string if author is <code>null</code> or
1226
	 * titleCache is <code>null</code>.
1227
	 * @param author
1228
	 * @return
1229
	 */
1230
	private String getNomTitleNz(INomenclaturalAuthor author) {
1231
		if (author != null){
1232
			return CdmUtils.Nz(author.getNomenclaturalTitle());
1233
		}else{
1234
			return "";
1235
		}
1236
	}
1237

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

    
1258

    
1259

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

    
1278

    
1279

    
1280
	private boolean childExists_old(Taxon child, TaxonNode higherNode) {
1281
		for (TaxonNode childNode : higherNode.getChildNodes()){
1282
			String existingChildTitle = childNode.getTaxon().getName().getTitleCache();
1283
			String newChildTitle = child.getName().getTitleCache();
1284
			if (existingChildTitle.equals(newChildTitle)){
1285
				return true;
1286
			}
1287
		}
1288
		return false;
1289
	}
1290

    
1291

    
1292
}
(6-6/7)