Project

General

Profile

Download (47.8 KB) Statistics
| Branch: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

    
10
package eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns;
11

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

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

    
26
import eu.etaxonomy.cdm.api.service.IClassificationService;
27
import eu.etaxonomy.cdm.common.CdmUtils;
28
import eu.etaxonomy.cdm.io.common.IOValidator;
29
import eu.etaxonomy.cdm.io.common.TdwgAreaProvider;
30
import eu.etaxonomy.cdm.io.common.mapping.DbImportMapping;
31
import eu.etaxonomy.cdm.io.common.mapping.DbImportMethodMapper;
32
import eu.etaxonomy.cdm.io.common.mapping.DbImportTaxIncludedInMapper;
33
import eu.etaxonomy.cdm.io.common.mapping.IMappingImport;
34
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
35
import eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns.validation.CentralAfricaFernsTaxonImportValidator;
36
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
37
import eu.etaxonomy.cdm.model.common.CdmBase;
38
import eu.etaxonomy.cdm.model.common.Language;
39
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
40
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
41
import eu.etaxonomy.cdm.model.description.Distribution;
42
import eu.etaxonomy.cdm.model.description.Feature;
43
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
44
import eu.etaxonomy.cdm.model.description.TaxonDescription;
45
import eu.etaxonomy.cdm.model.description.TextData;
46
import eu.etaxonomy.cdm.model.location.Country;
47
import eu.etaxonomy.cdm.model.location.NamedArea;
48
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
49
import eu.etaxonomy.cdm.model.location.NamedAreaType;
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 final Map<String, UUID> nameCacheTaxonMap = new HashMap<String, UUID>();
80
	private final 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
	@Override
91
	protected String getIdQuery() {
92
		String strQuery = " SELECT [Taxon number] FROM " + dbTableName;;
93
		return strQuery;
94
	}
95

    
96
	@Override
97
	protected DbImportMapping<?,?> getMapping() {
98
		if (mapping == null){
99
			mapping = new DbImportMapping();
100

    
101
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "createObject", ResultSet.class, CentralAfricaFernsImportState.class));
102
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapCommonName", ResultSet.class, CentralAfricaFernsImportState.class));
103
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapDistribution", ResultSet.class, CentralAfricaFernsImportState.class ));
104
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapEcology", ResultSet.class, CentralAfricaFernsImportState.class));
105

    
106
		}
107
		return mapping;
108
	}
109

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

    
123

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

    
135

    
136
	/**
137
	 * Fills the nameCache and the titleCache maps. The maps are used to find existing taxa
138
	 * by titleCache or nameCache matching.
139
	 * Matching may be implemented more sophisticated in future versions.
140
	 */
141
	private void fillTaxonMap() {
142
		List<String> propPath = Arrays.asList(new String []{"name"});
143

    
144
		List<Taxon> taxonList = getTaxonService().list(Taxon.class, null, null, null, propPath );
145
		for (Taxon taxon : taxonList){
146
			NonViralName<?> nvn = CdmBase.deproxy(taxon.getName(), NonViralName.class);
147
			UUID uuid = taxon.getUuid();
148
			String nameCache = nvn.getNameCache();
149
			String titleCache = nvn.getTitleCache();
150
			nameCacheTaxonMap.put(nameCache, uuid);
151
			titleCacheTaxonMap.put(titleCache, uuid);
152
		}
153
	}
154

    
155

    
156
	@Override
157
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, CentralAfricaFernsImportState state) {
158
		String nameSpace;
159
		Class<?> cdmClass;
160
		Set<String> idSet;
161
		Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<Object, Map<String, ? extends CdmBase>>();
162

    
163
		try{
164
			Set<String> taxonIdSet = new HashSet<String>();
165
//				Set<String> referenceIdSet = new HashSet<String>();
166
			while (rs.next()){
167
				handleForeignKey(rs, taxonIdSet, "Current");
168
				handleForeignKey(rs, taxonIdSet, "Taxon number");
169
//				handleForeignKey(rs, referenceIdSet, "PTRefFk");
170
			}
171

    
172
			//taxon map
173
			nameSpace = TAXON_NAMESPACE;
174
			cdmClass = TaxonBase.class;
175
			Map<String, TaxonBase> taxonMap = (Map<String, TaxonBase>)getCommonService().getSourcedObjectsByIdInSource(cdmClass, taxonIdSet, nameSpace);
176
			result.put(nameSpace, taxonMap);
177

    
178

    
179
			//reference map
180
			this.sourceReference = getFernsSourceReference(state);
181
//			nameSpace = "Reference";
182
//			cdmClass = Reference.class;
183
//			Map<String, Person> referenceMap = (Map<String, Person>)getCommonService().getSourcedObjectsByIdInSource(Person.class, teamIdSet, nameSpace);
184
//			result.put(Reference.class, referenceMap);
185

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

    
192

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

    
211
			return result;
212
		} catch (Exception e) {
213
			e.printStackTrace();
214
			return result;
215
		}
216

    
217
	}
218

    
219

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

    
243

    
244
	/**
245
	 * Handles records with status synonym. A synonym relationship to the accepted taxon
246
	 * is created.
247
	 * @param rs
248
	 * @param state
249
	 * @return
250
	 * @throws SQLException
251
	 */
252
	private Synonym handleSynonym(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException {
253
		String accTaxonId = rs.getString("Current");
254
		String nomRemarksString = rs.getString("Current/Synonym");
255

    
256
		String synonymId = state.getTaxonNumber();
257
		Synonym synonym = (Synonym)state.getRelatedObject(TAXON_NAMESPACE, synonymId);
258
		if (synonym == null){
259
			logger.warn ("Synonym ("+synonymId+")not found.");
260
			return null;
261
		}
262
		TaxonBase<?> taxonBase = CdmBase.deproxy(state.getRelatedObject(TAXON_NAMESPACE, accTaxonId), TaxonBase.class);
263

    
264
		if (taxonBase != null){
265
			if (taxonBase.isInstanceOf(Taxon.class)){
266
				Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
267
				SynonymRelationship rel = taxon.addSynonym(synonym, SynonymRelationshipType.SYNONYM_OF());
268
				if ("p.p.".equalsIgnoreCase(nomRemarksString)){
269
					rel.setProParte(true);
270
				}
271
			}else{
272
				logger.warn("Accepted taxon (" + accTaxonId + ") for synonym (" + synonymId +") is not of type 'Current'");
273
			}
274
		}else{
275
			logger.warn("Taxon (" + accTaxonId + ") not found for synonym (" + synonymId +")");
276
		}
277

    
278
		return synonym;
279
	}
280

    
281

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

    
316
		makeNextHigherTaxon(state, rs, child, epithets);
317
		return child;
318
	}
319

    
320

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

    
332
		Taxon constructedHigherTaxon = constructNextHigherTaxon(state, rs, child, epithets);
333
		Reference citation = null;
334
		String microcitation = null;
335

    
336
		if (constructedHigherTaxon != null){
337
			handleHigherTaxonMustExist(state, rs, child, epithets, constructedHigherTaxon, citation, microcitation);
338
		}else{
339
			//add taxon to tree if not yet added
340
			if (child.getTaxonNodes().size() == 0){
341
				makeTaxonomicallyIncluded(state, null, child, null, citation, microcitation);
342
			}
343
		}
344
	}
345

    
346

    
347

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

    
396

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

    
420

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

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

    
460

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

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

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

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

    
548

    
549
	/**
550
	 * Checks if the name caches match.
551
	 * @param name1
552
	 * @param name2
553
	 * @return
554
	 */
555
	private boolean nameCachesMatch(BotanicalName name1, BotanicalName name2) {
556
		return CdmUtils.nullSafeEqual(name1.getNameCache(), name2.getNameCache());
557
	}
558

    
559
	/**
560
	 * Checks if the name caches of the related names match.
561
	 *@param taxon1
562
	 * @param taxon2
563
	 * @return
564
	 */
565
	private boolean nameCachesMatch(Taxon taxon1, Taxon taxon2) {
566
		BotanicalName name1 = CdmBase.deproxy(taxon1.getName(), BotanicalName.class);
567
		BotanicalName name2 = CdmBase.deproxy(taxon2.getName(), BotanicalName.class);
568
		return nameCachesMatch(name1, name2);
569
	}
570

    
571

    
572
	/**
573
	 * Checks if all authors match
574
	 * @param name1
575
	 * @param name2
576
	 * @return
577
	 */
578
	private boolean authorsMatch(NonViralName<?> name1, NonViralName<?> name2) {
579
		String combinationAuthor1 = name1.computeCombinationAuthorNomenclaturalTitle();
580
		String combinationAuthor2 = name2.computeCombinationAuthorNomenclaturalTitle();
581
		String basionymAuthor1 = name1.computeBasionymAuthorNomenclaturalTitle();
582
		String basionymAuthor2 = name2.computeBasionymAuthorNomenclaturalTitle();
583
		String exCombinationAuthor1 = name1.computeExCombinationAuthorNomenclaturalTitle();
584
		String exCombinationAuthor2 = name2.computeExCombinationAuthorNomenclaturalTitle();
585
		String exBasionymAuthor1 = name1.computeExBasionymAuthorNomenclaturalTitle();
586
		String exBasionymAuthor2 = name2.computeExBasionymAuthorNomenclaturalTitle();
587
		boolean result =
588
			CdmUtils.nullSafeEqual(combinationAuthor1, combinationAuthor2) &&
589
			CdmUtils.nullSafeEqual(basionymAuthor1, basionymAuthor2) &&
590
			CdmUtils.nullSafeEqual(exCombinationAuthor1, exCombinationAuthor2) &&
591
			CdmUtils.nullSafeEqual(exBasionymAuthor1, exBasionymAuthor2);
592
		return result;
593
	}
594

    
595
	/**
596
	 * Checks if all authors of the related names match.
597
	 * @param taxon1
598
	 * @param taxon2
599
	 * @return
600
	 */
601
	private boolean authorsMatch(Taxon taxon1, Taxon taxon2) {
602
		BotanicalName name1 = CdmBase.deproxy(taxon1.getName(), BotanicalName.class);
603
		BotanicalName name2 = CdmBase.deproxy(taxon2.getName(), BotanicalName.class);
604
		return authorsMatch(name1, name2);
605
	}
606

    
607
	/**
608
	 * Compares ranks of 2 names.
609
	 * @param parentName
610
	 * @param constructedName
611
	 * @return
612
	 */
613
	private int compareRanks(BotanicalName name1, BotanicalName name2) {
614
		return name1.getRank().compareTo(name2.getRank());
615
	}
616

    
617
	/**
618
	 * Compares the ranks of the according names.
619
	 * @param taxon1
620
	 * @param taxon2
621
	 * @return
622
	 */
623
	private int compareRanks(Taxon taxon1, Taxon taxon2) {
624
		BotanicalName name1 = CdmBase.deproxy(taxon1.getName(), BotanicalName.class);
625
		BotanicalName name2 = CdmBase.deproxy(taxon2.getName(), BotanicalName.class);
626
		return compareRanks(name1, name2);
627
	}
628

    
629

    
630

    
631
	/**
632
	 * Checks if 2 names match.
633
	 * Current strategy: true, if ranks are equal, nameCaches match and authors match
634
	 * @param name1
635
	 * @param name2
636
	 * @return
637
	 */
638
	private boolean namesMatch(BotanicalName name1, BotanicalName name2) {
639
		return compareRanks(name1, name2)==0 && nameCachesMatch(name1, name2) && authorsMatch(name1, name2);
640
	}
641

    
642
	/**
643
	 * Checks if the according names match.
644
	 * @see #namesMatch(BotanicalName, BotanicalName)
645
	 * @param taxon1
646
	 * @param taxon2
647
	 * @return
648
	 */
649
	private boolean namesMatch(Taxon taxon1, Taxon taxon2) {
650
		BotanicalName name1 = CdmBase.deproxy(taxon1.getName(), BotanicalName.class);
651
		BotanicalName name2 = CdmBase.deproxy(taxon2.getName(), BotanicalName.class);
652
		return namesMatch(name1, name2);
653
	}
654

    
655

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

    
681

    
682
	/**
683
	 * Persists and saves the newly created taxon to the CDM store and to the look-up
684
	 * maps.
685
	 * @param state
686
	 * @param constructedHigherTaxon
687
	 * @return
688
	 */
689
	private Taxon saveConstructedTaxon(CentralAfricaFernsImportState state, Taxon constructedHigherTaxon) {
690
		BotanicalName constructedName = CdmBase.deproxy(constructedHigherTaxon.getName(), BotanicalName.class);
691
		String nameCache = constructedName.getNameCache();
692
		String titleCache = constructedName.getTitleCache();
693
		nameCacheTaxonMap.put(nameCache, constructedHigherTaxon.getUuid());
694
		titleCacheTaxonMap.put(titleCache, constructedHigherTaxon.getUuid());
695
		state.addRelatedObject(HIGHER_TAXON_NAMESPACE, nameCache, constructedHigherTaxon);
696

    
697
		//persist
698
//		Reference citation = state.getConfig().getSourceReference(); //throws nonUniqueObject exception
699
		Reference citation = null;
700
		String id = state.getTaxonNumber() + "-" + constructedName.getRank().getTitleCache();
701
		addOriginalSource(constructedName, id, NAME_NAMESPACE, citation);
702
		addOriginalSource(constructedHigherTaxon, id, TAXON_NAMESPACE, citation);
703
		getTaxonService().save(constructedHigherTaxon);
704

    
705
		return constructedHigherTaxon;
706
	}
707

    
708

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

    
746
		TaxonNode childNode;
747
		if (parent != null){
748
			childNode = tree.addParentChild(parent, child, citation, microCitation);
749
		}else{
750
			childNode = tree.addChildTaxon(child, citation, microCitation);
751
		}
752
		return (childNode != null);
753
	}
754

    
755

    
756
	/**
757
	 * Reasons if a higher taxon should exist. If it should exist it returns it as a new taxon.
758
	 * Returns null otherwise.
759
	 * @return
760
	 * @throws SQLException
761
	 */
762
	private Taxon constructNextHigherTaxon(CentralAfricaFernsImportState state, ResultSet rs, Taxon childTaxon, Epithets epithets) throws SQLException {
763

    
764
		Taxon result = null;
765
		BotanicalName childName = CdmBase.deproxy(childTaxon.getName(), BotanicalName.class);
766
		Rank childRank = childName.getRank();
767
		BotanicalName higherName;
768
		higherName = handleInfraSpecific(childRank, epithets);
769
		if (higherName.getRank() == null){
770
			handleSpecies(childRank, higherName,  epithets);
771
		}
772
		if (higherName.getRank() == null){
773
			handleInfraGeneric(childRank, higherName, epithets);
774
		}
775
		if (higherName.getRank() == null){
776
			handleUninomial(childRank, higherName, epithets);
777
		}
778

    
779
		if (higherName.getRank() != null){
780
			result = Taxon.NewInstance(higherName, childTaxon.getSec());
781
			//TODO correct??
782
			setAuthor(higherName, rs, state.getTaxonNumber(), true);
783
//			UUID uuid = higherName.getUuid();
784
//			String name = higherName.getNameCache();
785
//			taxonMap.put(name, uuid);
786
//			state.addRelatedObject(HIGHER_TAXON_NAMESPACE, higherName.getNameCache(), result);
787
		}
788
		return result;
789
	}
790

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

    
793
		BotanicalName taxonName = BotanicalName.NewInstance(null);
794
		Rank newRank = null;
795

    
796
		if (StringUtils.isNotBlank(epithets.subFormaName)   && lowerTaxonRank.isLower(Rank.SUBFORM())){
797
			taxonName.setInfraSpecificEpithet(epithets.subFormaName);
798
			newRank =  Rank.SUBFORM();
799
		}else if (StringUtils.isNotBlank(epithets.formaName)  && lowerTaxonRank.isLower(Rank.FORM())){
800
			taxonName.setInfraSpecificEpithet(epithets.formaName);
801
			newRank =  Rank.FORM();
802
		}else if (StringUtils.isNotBlank(epithets.subVariety)  && lowerTaxonRank.isLower(Rank.SUBVARIETY())){
803
			taxonName.setInfraSpecificEpithet(epithets.subVariety);
804
			newRank =  Rank.SUBVARIETY();
805
		}else if (StringUtils.isNotBlank(epithets.varietyName)  && lowerTaxonRank.isLower(Rank.VARIETY())){
806
			taxonName.setInfraSpecificEpithet(epithets.varietyName);
807
			newRank =  Rank.VARIETY();
808
		}else if (StringUtils.isNotBlank(epithets.subspeciesName)  && lowerTaxonRank.isLower(Rank.SUBSPECIES())){
809
			taxonName.setInfraSpecificEpithet(epithets.subspeciesName);
810
			newRank = Rank.SUBSPECIES();
811
		}
812

    
813
		if (newRank != null){
814
			taxonName.setSpecificEpithet(epithets.specificEpithet);
815
			taxonName.setGenusOrUninomial(epithets.genusName);
816
			taxonName.setRank(newRank);
817
		}
818

    
819
		return taxonName;
820
	}
821

    
822
	private BotanicalName handleSpecies(Rank lowerTaxonRank, BotanicalName taxonName, Epithets epithets) {
823
		Rank newRank = null;
824

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

    
836
	private BotanicalName handleInfraGeneric(Rank lowerTaxonRank, BotanicalName taxonName, Epithets epithets) {
837
		Rank newRank = null;
838

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

    
859

    
860

    
861
	private BotanicalName handleUninomial(Rank lowerTaxonRank, BotanicalName taxonName,  Epithets epithets) {
862

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

    
890

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

    
914

    
915
	/**
916
	 * for internal use only, used by MethodMapper
917
	 */
918
	private TaxonBase mapDistribution(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException{
919
		try {
920
			String taxonNumber = state.getTaxonNumber();
921
//			logger.info(taxonNumber);
922
			TaxonBase<?> taxonBase = state.getRelatedObject(state.CURRENT_OBJECT_NAMESPACE, state.CURRENT_OBJECT_ID, TaxonBase.class);
923
			String countriesString = rs.getString("Distribution - Country");
924
			String province = rs.getString("Distribution - Province");
925
			String distributionDetailed = rs.getString("Distribution - detailed");
926
			if (taxonBase != null){
927
				TaxonNameBase<?,?> nameUsedInSource = taxonBase.getName();
928
				Taxon taxon = getAcceptedTaxon(taxonBase);
929
				if (taxon != null){
930

    
931
					if (StringUtils.isNotBlank(countriesString) ){
932
						makeCountries(state, taxonNumber, taxon, nameUsedInSource, countriesString, province, distributionDetailed);
933
					}
934
					makeProvince(taxon, province);
935
					makeDistributionDetailed(taxon, distributionDetailed);
936
				}else{
937
					logger.warn(taxonNumber + " - Accepted taxon for synonym can't be defined for distribution. Synonym " + taxonBase.getName().getTitleCache());
938
				}
939
			}else{
940
				logger.warn(" - " + taxonNumber + ": TaxonBase was null");
941
			}
942
			return taxonBase;
943
		} catch (Exception e) {
944
			e.printStackTrace();
945
			return null;
946
		}
947
	}
948

    
949

    
950
	/**
951
	 * for internal use only, used by MethodMapper
952
	 * @param commonNames
953
	 */
954
	private TaxonBase mapEcology(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException{
955
		String taxonNumber = state.getTaxonNumber();
956
		String ecologyString = rs.getString("Ecology");
957
		TaxonBase<?> taxonBase = state.getRelatedObject(state.CURRENT_OBJECT_NAMESPACE, state.CURRENT_OBJECT_ID, TaxonBase.class);
958
		if (StringUtils.isNotBlank(ecologyString)){
959
			Taxon taxon = getAcceptedTaxon(taxonBase);
960

    
961
			if (taxon != null){
962
				TaxonDescription description = getTaxonDescription(taxon, false, true);
963
				TextData ecology = TextData.NewInstance(Feature.ECOLOGY());
964
				ecology.putText(Language.ENGLISH(), ecologyString.trim());
965
				description.addElement(ecology);
966
			}else{
967
				logger.warn(taxonNumber + " - Accepted taxon for synonym can't be defined for ecology. Synonym " + taxonBase.getName().getTitleCache());
968
			}
969
		}
970
		return taxonBase;
971
	}
972

    
973

    
974

    
975

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

    
985

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

    
995

    
996
	/**
997
	 * @param state
998
	 * @param taxonNumber
999
	 * @param taxonBase
1000
	 * @param countriesString
1001
	 */
1002
	private void makeCountries(CentralAfricaFernsImportState state, String taxonNumber, Taxon taxon, TaxonNameBase nameUsedInSource, String countriesString, String province, String distributionDetailed) {
1003
		countriesString = countriesString.replaceAll("\\*", "");
1004
		countriesString = countriesString.replace("  ", " ");
1005
		countriesString = countriesString.replace(", endemic", " - endemic");
1006
		countriesString = countriesString.replace("(endemic)", " - endemic");
1007
		countriesString = countriesString.replace("(introduced)", " - introduced");
1008
		countriesString = countriesString.replace("(naturalised)", " - naturalised");
1009
		countriesString = countriesString.replace("Madagascar-", "Madagascar -");
1010
		countriesString = countriesString.replace("Mah\u00e9", "Mahe");
1011

    
1012
		String[] split = countriesString.split("[,;]");
1013
		String remainingString = null;
1014
		for (String countryString : split){
1015
			countryString = CdmUtils.concat(", ", remainingString , countryString);
1016
			if (countryString.matches(".*\\(.*") && ! countryString.matches(".*\\).*")){
1017
				remainingString = countryString;
1018
				continue;
1019
			}
1020
			remainingString = null;
1021
			try {
1022
				makeSingleCountry(state, taxonNumber, taxon, nameUsedInSource, countryString.trim());
1023
			} catch (UndefinedTransformerMethodException e) {
1024
				e.printStackTrace();
1025
			}
1026
		}
1027
	}
1028

    
1029

    
1030
	private void makeSingleCountry(CentralAfricaFernsImportState state, String taxonNumber, Taxon taxon, TaxonNameBase nameUsedInSource, String country) throws UndefinedTransformerMethodException {
1031
		boolean areaDoubtful = false;
1032
		Distribution distribution = Distribution.NewInstance(null, PresenceAbsenceTerm.PRESENT());
1033
		Reference sourceReference = this.sourceReference;
1034
		distribution.addSource(OriginalSourceType.Import, taxonNumber, "Distribution_Country", sourceReference, null, nameUsedInSource, null);
1035
		NamedArea area = null;
1036
		//empty
1037
		if (StringUtils.isBlank(country)){
1038
			return;
1039
		}
1040
		country = country.trim();
1041
		//doubtful
1042
		if (country.startsWith("?")){
1043
			areaDoubtful = true;
1044
			country = country.substring(1).trim();
1045
		}
1046
		//status
1047
		country = makeCountryStatus(state, country, distribution);
1048

    
1049
		//brackets
1050
		country = makeCountryBrackets(state, taxonNumber, taxon, nameUsedInSource, country);
1051
		String countryWithoutIslands = null;
1052
		String countryWithoutDot = null;
1053
		if (country.endsWith(" Isl.") || country.endsWith(" isl.") ){
1054
			countryWithoutIslands = country.substring(0, country.length()-5);
1055
		}
1056
		if (country.endsWith(".")){
1057
			countryWithoutDot = country.substring(0, country.length()-1);
1058
		}
1059
		if (country.endsWith("*")){
1060
			country = country.substring(0, country.length()-1);
1061
		}
1062
		if (country.endsWith("Islands")){
1063
			country = country.replace("Islands", "Is.");
1064
		}
1065

    
1066

    
1067
		//areas
1068
		if (TdwgAreaProvider.isTdwgAreaLabel(country)){
1069
			//tdwg
1070
			area = TdwgAreaProvider.getAreaByTdwgLabel(country);
1071
		}else if (TdwgAreaProvider.isTdwgAreaLabel(countryWithoutIslands)){
1072
			//tdwg
1073
			area = TdwgAreaProvider.getAreaByTdwgLabel(countryWithoutIslands);
1074
		}else if (TdwgAreaProvider.isTdwgAreaLabel(countryWithoutDot)){
1075
			//tdwg
1076
			area = TdwgAreaProvider.getAreaByTdwgLabel(countryWithoutDot);
1077
		}else if ( (area = state.getTransformer().getNamedAreaByKey(country)) != null) {
1078
			//area already set
1079
		}else if (Country.isCountryLabel(country)){
1080
			//iso
1081
			area = Country.getCountryByLabel(country);
1082
		}else{
1083
			//others
1084
			NamedAreaLevel level = null;
1085
			NamedAreaType areaType = null;
1086

    
1087
			UUID uuid = state.getTransformer().getNamedAreaUuid(country);
1088
			if (uuid == null){
1089
				logger.error(taxonNumber + " - Unknown country: " + country);
1090
			}
1091
			area = getNamedArea(state, uuid, country, country, country, areaType, level);
1092
		}
1093

    
1094
		distribution.setArea(area);
1095
		if (areaDoubtful == true){
1096
			if (distribution.getStatus().equals(PresenceAbsenceTerm.PRESENT())){
1097
				distribution.setStatus(PresenceAbsenceTerm.PRESENT_DOUBTFULLY());
1098
			}
1099
		}
1100
		TaxonDescription description = getTaxonDescription(taxon, false, true);
1101
		description.addElement(distribution);
1102
	}
1103

    
1104

    
1105

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

    
1119

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

    
1134
	private String makeCountryStatus(CentralAfricaFernsImportState state, String country, Distribution distribution) throws UndefinedTransformerMethodException {
1135
		PresenceAbsenceTerm status = null;
1136
		String[] split = country.split(" - ");
1137

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

    
1159

    
1160

    
1161

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

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

    
1179

    
1180

    
1181
//************************ OLD **********************************************************
1182

    
1183
	/**
1184
	 * Adds the higherTaxon authors to the existingHigherTaxon authors if the higherTaxon has authors and
1185
	 * the existingHigherTaxon has no authors.
1186
	 * Returns false if both taxa have authors and the authors differ from each other.
1187
	 * @param higherTaxon
1188
	 * @param existingHigherTaxon
1189
	 */
1190
	private boolean mergeAuthors_old(Taxon higherTaxon, Taxon existingHigherTaxon) {
1191
		NonViralName<?> existingName = CdmBase.deproxy(higherTaxon.getName(), NonViralName.class);
1192
		NonViralName<?> newName = CdmBase.deproxy(existingHigherTaxon.getName(), NonViralName.class);
1193
		if (existingName == newName){
1194
			return true;
1195
		}
1196
		if (! newName.hasAuthors()){
1197
			return true;
1198
		}
1199
		if (! existingName.hasAuthors()){
1200
			existingName.setCombinationAuthorship(newName.getCombinationAuthorship());
1201
			existingName.setExCombinationAuthorship(newName.getExCombinationAuthorship());
1202
			existingName.setBasionymAuthorship(newName.getBasionymAuthorship());
1203
			existingName.setExBasionymAuthorship(newName.getExBasionymAuthorship());
1204
			return true;
1205
		}
1206
		boolean authorsAreSame = true;
1207
		authorsAreSame &= getNomTitleNz(existingName.getCombinationAuthorship()).equals(getNomTitleNz(newName.getCombinationAuthorship()));
1208
		authorsAreSame &= getNomTitleNz(existingName.getExCombinationAuthorship()).equals(getNomTitleNz(newName.getExCombinationAuthorship()));
1209
		authorsAreSame &= getNomTitleNz(existingName.getBasionymAuthorship()).equals(getNomTitleNz(newName.getBasionymAuthorship()));
1210
		authorsAreSame &= getNomTitleNz(existingName.getExBasionymAuthorship()).equals(getNomTitleNz(newName.getExBasionymAuthorship()));
1211
		return authorsAreSame;
1212

    
1213

    
1214
	}
1215

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

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

    
1250

    
1251

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

    
1270

    
1271

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

    
1283

    
1284
}
(6-6/7)