Project

General

Profile

Download (48.2 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.PresenceAbsenceTermBase;
42
import eu.etaxonomy.cdm.model.description.PresenceTerm;
43
import eu.etaxonomy.cdm.model.description.TaxonDescription;
44
import eu.etaxonomy.cdm.model.description.TextData;
45
import eu.etaxonomy.cdm.model.location.NamedArea;
46
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
47
import eu.etaxonomy.cdm.model.location.NamedAreaType;
48
import eu.etaxonomy.cdm.model.location.TdwgArea;
49
import eu.etaxonomy.cdm.model.location.WaterbodyOrCountry;
50
import eu.etaxonomy.cdm.model.name.BotanicalName;
51
import eu.etaxonomy.cdm.model.name.NonViralName;
52
import eu.etaxonomy.cdm.model.name.Rank;
53
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
54
import eu.etaxonomy.cdm.model.reference.Reference;
55
import eu.etaxonomy.cdm.model.taxon.Classification;
56
import eu.etaxonomy.cdm.model.taxon.Synonym;
57
import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
58
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
59
import eu.etaxonomy.cdm.model.taxon.Taxon;
60
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
61
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
62

    
63

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

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

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

    
82
	private CentralAfricaFernsImportState state;
83

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

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

    
100

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

    
114
		}
115
		return mapping;
116
	}
117

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

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

    
143

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

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

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

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

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

    
226
	}
227

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

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

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

    
329

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

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

    
355

    
356

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

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

    
429

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

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

    
469

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

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

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

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

    
557

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

    
580

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

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

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

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

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

    
717

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

    
764

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

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

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

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

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

    
868

    
869

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

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

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

    
982

    
983

    
984

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

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

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

    
1038

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

    
1113

    
1114

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

    
1128

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

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

    
1168

    
1169
	
1170

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

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

    
1188
	
1189
	
1190
//************************ OLD **********************************************************
1191

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

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

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

    
1259

    
1260

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

    
1279

    
1280

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

    
1292

    
1293
}
(6-6/7)