Project

General

Profile

Download (45 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
package eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns;
10

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

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

    
25
import eu.etaxonomy.cdm.api.service.IClassificationService;
26
import eu.etaxonomy.cdm.common.CdmUtils;
27
import eu.etaxonomy.cdm.io.common.IOValidator;
28
import eu.etaxonomy.cdm.io.common.TdwgAreaProvider;
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.PresenceAbsenceTerm;
42
import eu.etaxonomy.cdm.model.description.TaxonDescription;
43
import eu.etaxonomy.cdm.model.description.TextData;
44
import eu.etaxonomy.cdm.model.location.Country;
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.name.IBotanicalName;
49
import eu.etaxonomy.cdm.model.name.INonViralName;
50
import eu.etaxonomy.cdm.model.name.Rank;
51
import eu.etaxonomy.cdm.model.name.TaxonName;
52
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
53
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
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.SynonymType;
58
import eu.etaxonomy.cdm.model.taxon.Taxon;
59
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
60
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
61

    
62
/**
63
 * @author a.mueller
64
 */
65
@Component
66
public class CentralAfricaFernsTaxonRelationImport
67
        extends CentralAfricaFernsImportBase<TaxonBase>
68
        implements IMappingImport<TaxonBase, CentralAfricaFernsImportState>{
69

    
70
    private static final long serialVersionUID = 5561099127416844593L;
71
    private static final Logger logger = Logger.getLogger(CentralAfricaFernsTaxonRelationImport.class);
72

    
73
	private DbImportMapping<?,?> mapping;
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<>();
80
	private final Map<String, UUID> titleCacheTaxonMap = new HashMap<>();
81

    
82
	public CentralAfricaFernsTaxonRelationImport(){
83
		super(pluralString, dbTableName, cdmTargetClass);
84
	}
85

    
86
	@Override
87
	protected String getIdQuery() {
88
		String strQuery = " SELECT [Taxon number] FROM " + dbTableName;;
89
		return strQuery;
90
	}
91

    
92
	@Override
93
	protected DbImportMapping<?,?> getMapping() {
94
		if (mapping == null){
95
			mapping = new DbImportMapping();
96

    
97
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "createObject", ResultSet.class, CentralAfricaFernsImportState.class));
98
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapCommonName", ResultSet.class, CentralAfricaFernsImportState.class));
99
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapDistribution", ResultSet.class, CentralAfricaFernsImportState.class ));
100
			mapping.addMapper(DbImportMethodMapper.NewInstance(this, "mapEcology", ResultSet.class, CentralAfricaFernsImportState.class));
101

    
102
		}
103
		return mapping;
104
	}
105

    
106
	@Override
107
	protected String getRecordQuery(CentralAfricaFernsImportConfigurator config) {
108
		String strSelect = " SELECT * ";
109
		String strFrom = " FROM [African pteridophytes] as ap";
110
		String strWhere = " WHERE ( ap.[taxon number] IN (" + ID_LIST_TOKEN + ") )";
111
		String strOrderBy = " ORDER BY [Taxon number]";
112
		String strRecordQuery = strSelect + strFrom + strWhere + strOrderBy ;
113
		return strRecordQuery;
114
	}
115

    
116
	@Override
117
	protected void doInvoke(CentralAfricaFernsImportState state) {
118
		fillTaxonMap();
119
		super.doInvoke(state);
120
		return;
121
	}
122

    
123
	/**
124
	 * Fills the nameCache and the titleCache maps. The maps are used to find existing taxa
125
	 * by titleCache or nameCache matching.
126
	 * Matching may be implemented more sophisticated in future versions.
127
	 */
128
	private void fillTaxonMap() {
129
		List<String> propPath = Arrays.asList(new String []{"name"});
130

    
131
		List<Taxon> taxonList = getTaxonService().list(Taxon.class, null, null, null, propPath );
132
		for (Taxon taxon : taxonList){
133
			INonViralName nvn = taxon.getName();
134
			UUID uuid = taxon.getUuid();
135
			String nameCache = nvn.getNameCache();
136
			String titleCache = nvn.getTitleCache();
137
			nameCacheTaxonMap.put(nameCache, uuid);
138
			titleCacheTaxonMap.put(titleCache, uuid);
139
		}
140
	}
141

    
142
	@Override
143
	public Map<Object, Map<String, ? extends CdmBase>> getRelatedObjectsForPartition(ResultSet rs, CentralAfricaFernsImportState state) {
144
		String nameSpace;
145
		Class<?> cdmClass;
146
		Set<String> idSet;
147
		Map<Object, Map<String, ? extends CdmBase>> result = new HashMap<Object, Map<String, ? extends CdmBase>>();
148

    
149
		try{
150
			Set<String> taxonIdSet = new HashSet<String>();
151
//				Set<String> referenceIdSet = new HashSet<String>();
152
			while (rs.next()){
153
				handleForeignKey(rs, taxonIdSet, "Current");
154
				handleForeignKey(rs, taxonIdSet, "Taxon number");
155
//				handleForeignKey(rs, referenceIdSet, "PTRefFk");
156
			}
157

    
158
			//taxon map
159
			nameSpace = TAXON_NAMESPACE;
160
			cdmClass = TaxonBase.class;
161
			Map<String, TaxonBase> taxonMap = getCommonService().getSourcedObjectsByIdInSourceC((Class<TaxonBase>) cdmClass, taxonIdSet, nameSpace);
162
			result.put(nameSpace, taxonMap);
163

    
164
			//reference map
165
			this.sourceReference = getFernsSourceReference(state);
166
//			nameSpace = "Reference";
167
//			cdmClass = Reference.class;
168
//			Map<String, Person> referenceMap = (Map<String, Person>)getCommonService().getSourcedObjectsByIdInSource(Person.class, teamIdSet, nameSpace);
169
//			result.put(Reference.class, referenceMap);
170

    
171
		} catch (SQLException e) {
172
			throw new RuntimeException(e);
173
		}
174
		return result;
175
	}
176

    
177
	@Override
178
	public TaxonBase createObject(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException {
179
		TaxonBase<?> result = null;
180
		try {
181
			String status = rs.getString("Current/Synonym");
182
			String taxonNumber = rs.getString("Taxon number");
183
			state.setTaxonNumber(taxonNumber);
184
			if ("s".equalsIgnoreCase(status)){
185
				//synonym
186
				result = handleSynonym(rs, state);
187
			}else{
188
				//accepted Taxon
189
				result = handleTaxon(rs, state);
190
			}
191

    
192
			return result;
193
		} catch (Exception e) {
194
			e.printStackTrace();
195
			return result;
196
		}
197
	}
198

    
199
	/**
200
	 * Class to store all epithets of the database record. Maybe extended with business logic.
201
	 */
202
	private class Epithets{
203
		private String orderName;
204
		private String subOrderName;
205
		private String familyName;
206
		private String subFamilyName;
207
		private String tribusName;
208
		private String subTribusName;
209
		private String sectionName;
210
		private String subsectionName;
211
		private String genusName;
212
		private String subGenusName;
213
		private String seriesName;
214
		private String specificEpithet;
215
		private String subspeciesName;
216
		private String varietyName;
217
		private String subVariety;
218
		private String formaName;
219
		private String subFormaName;
220
	}
221

    
222
	/**
223
	 * Handles records with status synonym. The synonym is attached to the accepted taxon.
224
	 */
225
	private Synonym handleSynonym(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException {
226
		String accTaxonId = rs.getString("Current");
227
		String nomRemarksString = rs.getString("Current/Synonym");
228

    
229
		String synonymId = state.getTaxonNumber();
230
		Synonym synonym = (Synonym)state.getRelatedObject(TAXON_NAMESPACE, synonymId);
231
		if (synonym == null){
232
			logger.warn ("Synonym ("+synonymId+")not found.");
233
			return null;
234
		}
235
		TaxonBase<?> taxonBase = CdmBase.deproxy(state.getRelatedObject(TAXON_NAMESPACE, accTaxonId), TaxonBase.class);
236

    
237
		if (taxonBase != null){
238
			if (taxonBase.isInstanceOf(Taxon.class)){
239
				Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
240
				taxon.addSynonym(synonym, SynonymType.SYNONYM_OF());
241
				if ("p.p.".equalsIgnoreCase(nomRemarksString)){
242
					logger.error("Pro parte synonyms are currently not handled correctly anymore. Needs to be adapted also in TaxonImport");
243
				}
244
			}else{
245
				logger.warn("Accepted taxon (" + accTaxonId + ") for synonym (" + synonymId +") is not of type 'Current'");
246
			}
247
		}else{
248
			logger.warn("Taxon (" + accTaxonId + ") not found for synonym (" + synonymId +")");
249
		}
250

    
251
		return synonym;
252
	}
253

    
254

    
255
	/**
256
	 * Handles all records with status 'current'. Creates parent-child relationships to the
257
	 * higher taxa. Uses a complex algorithm to reuse existing higher taxa.
258
	 */
259
	private Taxon handleTaxon(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException {
260
		String taxonNumber = rs.getString("Taxon number");
261
		Taxon child = (Taxon)state.getRelatedObject(TAXON_NAMESPACE, taxonNumber);
262
		if (child == null){
263
			logger.warn("Taxon does not exist: " + taxonNumber);
264
			return null;
265
		}
266
		Epithets epithets = new Epithets();
267
		epithets.orderName = rs.getString("Order name");
268
		epithets.subOrderName = rs.getString("Suborder name");
269
		epithets.familyName = rs.getString("Family name");
270
		epithets.subFamilyName = rs.getString("Subfamily name");
271
		epithets.tribusName = rs.getString("Tribus name");
272
		epithets.subTribusName = rs.getString("Subtribus name");
273
		epithets.sectionName = rs.getString("Section name");
274
		epithets.subsectionName = rs.getString("Subsection name");
275
		epithets.genusName = rs.getString("Genus name");
276
		epithets.subGenusName = rs.getString("Subgenus name");
277
		epithets.seriesName = rs.getString("Series name");
278
		epithets.specificEpithet = rs.getString("Specific epihet");
279
		epithets.subspeciesName = rs.getString("Subspecies name");
280
		epithets.varietyName = rs.getString("Variety name");
281
		epithets.subVariety = rs.getString("Subvariery");
282
		epithets.formaName = rs.getString("Forma name");
283
		epithets.subFormaName = rs.getString("Subforma");
284

    
285
		makeNextHigherTaxon(state, rs, child, epithets);
286
		return child;
287
	}
288

    
289

    
290
	/**
291
	 * Adds recursively this taxon to the next higher taxon. If the taxon exists already
292
	 * the relationship is not added again.<BR>
293
	 * If the author is missing in the old taxon but not in the new taxon the
294
	 * old taxon will get the new taxons author.(NOT VALID ANY MORE)<BR>
295
	 * If authors differ a new taxon is created.<BR>
296
	 * If a higher taxon exists the method is called recursively on this taxon.
297
	 * @throws SQLException
298
	 */
299
	private void makeNextHigherTaxon(CentralAfricaFernsImportState state, ResultSet rs, Taxon child, Epithets epithets) throws SQLException {
300

    
301
		Taxon constructedHigherTaxon = constructNextHigherTaxon(state, rs, child, epithets);
302
		Reference citation = null;
303
		String microcitation = null;
304

    
305
		if (constructedHigherTaxon != null){
306
			handleHigherTaxonMustExist(state, rs, child, epithets, constructedHigherTaxon, citation, microcitation);
307
		}else{
308
			//add taxon to tree if not yet added
309
			if (child.getTaxonNodes().size() == 0){
310
				makeTaxonomicallyIncluded(state, null, child, null, citation, microcitation);
311
			}
312
		}
313
	}
314

    
315

    
316

    
317
	/**
318
	 * Handles the case when the database record has data for a taxon of a higher rank
319
	 * than the <code>child</code> taxon's rank.
320
	 */
321
	private void handleHigherTaxonMustExist(CentralAfricaFernsImportState state, ResultSet rs, Taxon child, Epithets epithets, Taxon constructedHigherTaxon, Reference citation, String microCitation) throws SQLException {
322
		Taxon parentTaxon = getParent(child);
323
		if (parentTaxon == null){
324
			//if no parent taxon exists
325
			Taxon existingTaxon = findExistingNonParentTaxon(state, constructedHigherTaxon);
326
			if (existingTaxon != null){
327
				//a taxon with same title cache or same name cache exists
328
				parentTaxon = mergeExistingAndConstructedTaxon(state, existingTaxon, constructedHigherTaxon);
329
			}else{
330
				parentTaxon = constructedHigherTaxon;
331
			}
332
			makeTaxonomicallyIncluded(state, null, child, parentTaxon, citation, microCitation);
333
		}else{
334
			//parent taxon exists
335
			if (namesMatch(parentTaxon, constructedHigherTaxon)){
336
				//parents match
337
				//TODO what if the higher taxonomy does not match
338
				parentTaxon = mergeExistingAndConstructedTaxon(state, parentTaxon, constructedHigherTaxon);
339
			}else if (compareRanks(parentTaxon, constructedHigherTaxon) != 0){
340
				//ranks unequal
341
				parentTaxon = handleUnequalRanks(parentTaxon, constructedHigherTaxon);
342
			}else if (! nameCachesMatch(parentTaxon, constructedHigherTaxon)){
343
				//nameCache not equal
344
				parentTaxon = handleUnequalNameCaches(parentTaxon, constructedHigherTaxon);
345
			}else if (! authorsMatch(parentTaxon, constructedHigherTaxon)){
346
				//nameCache not equal
347
				parentTaxon = handleUnequalAuthors(parentTaxon, constructedHigherTaxon);
348
			}
349
		}
350
		//save the parent taxon, if it is new
351
		if (parentTaxon == constructedHigherTaxon){
352
			saveConstructedTaxon(state, constructedHigherTaxon);
353
		}
354
		makeNextHigherTaxon(state, rs, parentTaxon, epithets);
355
	}
356

    
357

    
358
	/**
359
	 * Merges author information of the constructed taxon into the existing taxon.
360
	 * Returns the existing taxon.
361
	 */
362
	private Taxon mergeExistingAndConstructedTaxon(CentralAfricaFernsImportState state, Taxon existingTaxon, Taxon constructedTaxon) {
363
		INonViralName constructedName = constructedTaxon.getName();
364
		INonViralName existingName = existingTaxon.getName();
365
		if (constructedName.hasAuthors()){
366
			if (! existingName.hasAuthors()){
367
				logger.warn(state.getTaxonNumber() + " - Constrcucted name ("+constructedName.getTitleCache()+") has authors but existing name ("+existingName.getTitleCache()+") has no authors");
368
			}else if (! authorsMatch(constructedName, existingName)){
369
				logger.warn(state.getTaxonNumber() + " - Constrcucted name ("+constructedName.getTitleCache()+") and existing name ("+existingName.getTitleCache()+") have different authors");
370
			}else {
371
				//authors match and are not null
372
			}
373
		}
374
		// more?
375
		return existingTaxon;
376
	}
377

    
378

    
379
	/**
380
	 * Strategy for the decision if an existing parent or a constructed higher taxon should
381
	 * be taken as parent in case that the authors of the name differ somehow.
382
	 * Current strategy: use existing parent if constructed higher taxon has no authors
383
	 * at all. Use constructed taxon otherwise.
384
	 * @param existingParentTaxon
385
	 * @param constructedHigherTaxon
386
	 * @return
387
	 */
388
	private Taxon handleUnequalAuthors(Taxon existingParentTaxon, Taxon constructedHigherTaxon) {
389
		Taxon result;
390
		IBotanicalName existingName = existingParentTaxon.getName();
391
		IBotanicalName constructedName = constructedHigherTaxon.getName();
392
		//current strategy: if constructedName has no authors (and parentName has
393
		if (! constructedName.hasAuthors()){
394
			result = existingParentTaxon;
395
		}else if (! existingName.hasAuthors()){
396
			result = constructedHigherTaxon;
397
		}else{
398
			result = constructedHigherTaxon;
399
		}
400
		return result;
401
	}
402

    
403
	/**
404
	 * Strategy for the decision if an existing parent or a constructed higher taxon
405
	 * should be taken as parent in case that the name caches differ somehow.
406
	 * Current strategy: Not implemented. Always use constructed higher taxon.
407
	 * @param existingParentTaxon
408
	 * @param constructedHigherTaxon
409
	 * @return
410
	 */
411
	private Taxon handleUnequalNameCaches(Taxon parentTaxon, Taxon constructedHigherTaxon) {
412
		IBotanicalName parentName = parentTaxon.getName();
413
		IBotanicalName constructedName = constructedHigherTaxon.getName();
414
		logger.warn("handleUnequalNameCaches not yet implemented");
415
		return constructedHigherTaxon;
416
	}
417

    
418
	/**
419
	 * Handles the case that the existing parent taxon and the constructed parent taxon
420
	 * have a diffent rank. Returns the constructedHigherTaxon if no common grand parent exists.
421
	 * @param parentTaxon
422
	 * @param constructedHigherTaxon
423
	 * @return
424
	 */
425
	private Taxon handleUnequalRanks(Taxon parentTaxon, Taxon constructedHigherTaxon) {
426
		IBotanicalName parentName = parentTaxon.getName();
427
		IBotanicalName constructedName = constructedHigherTaxon.getName();
428
		int compare = compareRanks(parentName, constructedName);
429
		Taxon lowerTaxon = parentTaxon;
430
		Taxon grandParentTaxon = constructedHigherTaxon;
431
		if (compare < 0){
432
			lowerTaxon = constructedHigherTaxon;
433
			grandParentTaxon = parentTaxon;
434
		}
435
		Taxon commonGrandParent = checkIsGrandParent(lowerTaxon, grandParentTaxon);
436
		if (commonGrandParent != null){
437
			if (lowerTaxon == constructedHigherTaxon){
438
				//TODO merge
439
				logger.warn("Merge in between taxon not yet implemented");
440
			}
441
		}else{
442
			return constructedHigherTaxon;
443
		}
444
		return lowerTaxon;
445
	}
446

    
447
	/**
448
	 * Tries to find a taxon which matches the constructed taxon but is not a parent
449
	 * taxon of the constructed taxon's child (condition will not be checked).
450
	 * Returns null if no such taxon exists.
451
	 * @param constructedHigherTaxon
452
	 * @param state
453
	 * @return
454
	 */
455
	private Taxon findExistingNonParentTaxon(CentralAfricaFernsImportState state, Taxon constructedHigherTaxon) {
456
		IBotanicalName constructedName = constructedHigherTaxon.getName();
457
		String titleCache = constructedName.getTitleCache();
458
		String nameCache = constructedName.getNameCache();
459
		UUID existingUuid = titleCacheTaxonMap.get(titleCache);
460
		if (existingUuid == null){
461
			existingUuid = nameCacheTaxonMap.get(nameCache);
462
		}
463
		Taxon relatedTaxon = null;
464
		if (existingUuid != null){
465
			relatedTaxon = state.getRelatedObject(HIGHER_TAXON_NAMESPACE, nameCache, Taxon.class);
466
			if (relatedTaxon == null){
467
				//TODO find for partition
468
				relatedTaxon = (Taxon)getTaxonService().find(existingUuid);
469
				if (relatedTaxon == null){
470
					logger.info(state.getTaxonNumber() +  " - Could not find existing name ("+nameCache+") in related objects map");
471
				}else{
472
					state.addRelatedObject(HIGHER_TAXON_NAMESPACE, nameCache, relatedTaxon);
473
				}
474
			}
475
		}
476
		return relatedTaxon;
477
	}
478

    
479
	/**
480
	 * Checks if a taxon is a grand parent of another taxon
481
	 */
482
	private Taxon checkIsGrandParent(Taxon childTaxon, Taxon grandParentTaxon) {
483
		IBotanicalName lowerName = childTaxon.getName();
484
		IBotanicalName higherName = CdmBase.deproxy(grandParentTaxon.getName());
485

    
486
		//TODO was wenn lowerTaxon constructed ist
487
		logger.warn("checkIsGrandParent not yet fully implemented");
488
		Taxon nextParent = getParent(childTaxon);
489
		if (namesMatch(nextParent, grandParentTaxon)){
490
			//TODO which one to return? Merging ?
491
			logger.warn("checkIsGrandParent(matching) not yet fully implemented");
492
			return grandParentTaxon;
493
		}else{
494
			if (compareRanks(lowerName, higherName) >= 0){
495
				return null;
496
			}else{
497
				return checkIsGrandParent(childTaxon, grandParentTaxon);
498
			}
499
		}
500
	}
501

    
502
	/**
503
	 * Checks if the name caches match.
504
	 */
505
	private boolean nameCachesMatch(IBotanicalName name1, IBotanicalName name2) {
506
		return CdmUtils.nullSafeEqual(name1.getNameCache(), name2.getNameCache());
507
	}
508

    
509
	/**
510
	 * Checks if the name caches of the related names match.
511
	 */
512
	private boolean nameCachesMatch(Taxon taxon1, Taxon taxon2) {
513
		IBotanicalName name1 = taxon1.getName();
514
		IBotanicalName name2 = taxon2.getName();
515
		return nameCachesMatch(name1, name2);
516
	}
517

    
518

    
519
	/**
520
	 * Checks if all authors match
521
	 */
522
	private boolean authorsMatch(INonViralName name1, INonViralName name2) {
523
		String combinationAuthor1 = name1.computeCombinationAuthorNomenclaturalTitle();
524
		String combinationAuthor2 = name2.computeCombinationAuthorNomenclaturalTitle();
525
		String basionymAuthor1 = name1.computeBasionymAuthorNomenclaturalTitle();
526
		String basionymAuthor2 = name2.computeBasionymAuthorNomenclaturalTitle();
527
		String exCombinationAuthor1 = name1.computeExCombinationAuthorNomenclaturalTitle();
528
		String exCombinationAuthor2 = name2.computeExCombinationAuthorNomenclaturalTitle();
529
		String exBasionymAuthor1 = name1.computeExBasionymAuthorNomenclaturalTitle();
530
		String exBasionymAuthor2 = name2.computeExBasionymAuthorNomenclaturalTitle();
531
		boolean result =
532
			CdmUtils.nullSafeEqual(combinationAuthor1, combinationAuthor2) &&
533
			CdmUtils.nullSafeEqual(basionymAuthor1, basionymAuthor2) &&
534
			CdmUtils.nullSafeEqual(exCombinationAuthor1, exCombinationAuthor2) &&
535
			CdmUtils.nullSafeEqual(exBasionymAuthor1, exBasionymAuthor2);
536
		return result;
537
	}
538

    
539
	/**
540
	 * Checks if all authors of the related names match.
541
	 */
542
	private boolean authorsMatch(Taxon taxon1, Taxon taxon2) {
543
		IBotanicalName name1 = taxon1.getName();
544
		IBotanicalName name2 = taxon2.getName();
545
		return authorsMatch(name1, name2);
546
	}
547

    
548
	/**
549
	 * Compares ranks of 2 names.
550
	 */
551
	private int compareRanks(IBotanicalName name1, IBotanicalName name2) {
552
		return name1.getRank().compareTo(name2.getRank());
553
	}
554

    
555
	/**
556
	 * Compares the ranks of the according names.
557
	 */
558
	private int compareRanks(Taxon taxon1, Taxon taxon2) {
559
		IBotanicalName name1 = taxon1.getName();
560
		IBotanicalName name2 = taxon2.getName();
561
		return compareRanks(name1, name2);
562
	}
563

    
564

    
565

    
566
	/**
567
	 * Checks if 2 names match.
568
	 * Current strategy: true, if ranks are equal, nameCaches match and authors match
569
	 */
570
	private boolean namesMatch(IBotanicalName name1, IBotanicalName name2) {
571
		return compareRanks(name1, name2)==0 && nameCachesMatch(name1, name2) && authorsMatch(name1, name2);
572
	}
573

    
574
	/**
575
	 * Checks if the according names match.
576
	 * @see #namesMatch(BotanicalName, BotanicalName)
577
	 */
578
	private boolean namesMatch(Taxon taxon1, Taxon taxon2) {
579
		IBotanicalName name1 = taxon1.getName();
580
		IBotanicalName name2 = taxon2.getName();
581
		return namesMatch(name1, name2);
582
	}
583

    
584

    
585
	/**
586
	 * Returns the only parent of the taxon. If not parent exists <code>null</code> is
587
	 * returned.
588
	 * TODO move to taxon class (with classification)
589
	 * @param child
590
	 * @return
591
	 * @throws IllegalStateException if taxon belongs to multiple states
592
	 */
593
	private Taxon getParent(Taxon child) {
594
		int countNodes = child.getTaxonNodes().size();
595
		if (countNodes < 1){
596
			return null;
597
		}else if (countNodes > 1){
598
			throw new IllegalStateException("Multiple nodes exist for child taxon. This is an invalid state for this import.");
599
		}else{
600
			TaxonNode childNode = child.getTaxonNodes().iterator().next();
601
			TaxonNode parentNode = childNode.getParent();
602
			if (parentNode != null){
603
				return parentNode.getTaxon();
604
			}else{
605
				return null;
606
			}
607
		}
608
	}
609

    
610
	/**
611
	 * Persists and saves the newly created taxon to the CDM store and to the look-up
612
	 * maps.
613
	 * @param state
614
	 * @param constructedHigherTaxon
615
	 * @return
616
	 */
617
	private Taxon saveConstructedTaxon(CentralAfricaFernsImportState state, Taxon constructedHigherTaxon) {
618
		IBotanicalName constructedName = constructedHigherTaxon.getName();
619
		String nameCache = constructedName.getNameCache();
620
		String titleCache = constructedName.getTitleCache();
621
		nameCacheTaxonMap.put(nameCache, constructedHigherTaxon.getUuid());
622
		titleCacheTaxonMap.put(titleCache, constructedHigherTaxon.getUuid());
623
		state.addRelatedObject(HIGHER_TAXON_NAMESPACE, nameCache, constructedHigherTaxon);
624

    
625
		//persist
626
//		Reference citation = state.getConfig().getSourceReference(); //throws nonUniqueObject exception
627
		Reference citation = null;
628
		String id = state.getTaxonNumber() + "-" + constructedName.getRank().getTitleCache();
629
		addOriginalSource(constructedName, id, NAME_NAMESPACE, citation);
630
		addOriginalSource(constructedHigherTaxon, id, TAXON_NAMESPACE, citation);
631
		getTaxonService().save(constructedHigherTaxon);
632

    
633
		return constructedHigherTaxon;
634
	}
635

    
636

    
637
	//TODO use Mapper
638
	/**
639
	 * Adds the parent child relationship. Creates and saves the classification if needed.
640
	 * Adds parent and child to the classification.
641
	 */
642
	private boolean makeTaxonomicallyIncluded(CentralAfricaFernsImportState state, Integer treeRefFk, Taxon child, Taxon parent, Reference citation, String microCitation){
643
		String treeKey;
644
		UUID treeUuid;
645
		if (treeRefFk == null){
646
			treeKey = "1";  // there is only one tree and it gets the map key '1'
647
			treeUuid = state.getConfig().getClassificationUuid();
648
		}else{
649
			treeKey =String.valueOf(treeRefFk);
650
			treeUuid = state.getTreeUuidByTreeKey(treeKey);
651
		}
652
		Classification tree = (Classification)state.getRelatedObject(DbImportTaxIncludedInMapper.TAXONOMIC_TREE_NAMESPACE, treeKey);
653
		if (tree == null){
654
			IClassificationService service = state.getCurrentIO().getClassificationService();
655
			tree = service.find(treeUuid);
656
			if (tree == null){
657
				String treeName = state.getConfig().getClassificationName();
658
				tree = Classification.NewInstance(treeName);
659
				tree.setUuid(treeUuid);
660
				//FIXME tree reference
661
				tree.setReference(citation);
662
				service.save(tree);
663
			}
664
			state.addRelatedObject(DbImportTaxIncludedInMapper.TAXONOMIC_TREE_NAMESPACE, treeKey, tree);
665
		}
666

    
667
		TaxonNode childNode;
668
		if (parent != null){
669
			childNode = tree.addParentChild(parent, child, citation, microCitation);
670
		}else{
671
			childNode = tree.addChildTaxon(child, citation, microCitation);
672
		}
673
		return (childNode != null);
674
	}
675

    
676

    
677
	/**
678
	 * Reasons if a higher taxon should exist. If it should exist it returns it as a new taxon.
679
	 * Returns null otherwise.
680
	 * @return
681
	 * @throws SQLException
682
	 */
683
	private Taxon constructNextHigherTaxon(CentralAfricaFernsImportState state, ResultSet rs, Taxon childTaxon, Epithets epithets) throws SQLException {
684

    
685
		Taxon result = null;
686
		IBotanicalName childName = CdmBase.deproxy(childTaxon.getName());
687
		Rank childRank = childName.getRank();
688
		IBotanicalName higherName;
689
		higherName = handleInfraSpecific(childRank, epithets);
690
		if (higherName.getRank() == null){
691
			handleSpecies(childRank, higherName,  epithets);
692
		}
693
		if (higherName.getRank() == null){
694
			handleInfraGeneric(childRank, higherName, epithets);
695
		}
696
		if (higherName.getRank() == null){
697
			handleUninomial(childRank, higherName, epithets);
698
		}
699

    
700
		if (higherName.getRank() != null){
701
			result = Taxon.NewInstance(higherName, childTaxon.getSec());
702
			//TODO correct??
703
			setAuthor(higherName, rs, state.getTaxonNumber(), true);
704
//			UUID uuid = higherName.getUuid();
705
//			String name = higherName.getNameCache();
706
//			taxonMap.put(name, uuid);
707
//			state.addRelatedObject(HIGHER_TAXON_NAMESPACE, higherName.getNameCache(), result);
708
		}
709
		return result;
710
	}
711

    
712
	private IBotanicalName handleInfraSpecific(Rank lowerTaxonRank, Epithets epithets) {
713

    
714
		IBotanicalName taxonName = TaxonNameFactory.NewBotanicalInstance(null);
715
		Rank newRank = null;
716

    
717
		if (StringUtils.isNotBlank(epithets.subFormaName)   && lowerTaxonRank.isLower(Rank.SUBFORM())){
718
			taxonName.setInfraSpecificEpithet(epithets.subFormaName);
719
			newRank =  Rank.SUBFORM();
720
		}else if (StringUtils.isNotBlank(epithets.formaName)  && lowerTaxonRank.isLower(Rank.FORM())){
721
			taxonName.setInfraSpecificEpithet(epithets.formaName);
722
			newRank =  Rank.FORM();
723
		}else if (StringUtils.isNotBlank(epithets.subVariety)  && lowerTaxonRank.isLower(Rank.SUBVARIETY())){
724
			taxonName.setInfraSpecificEpithet(epithets.subVariety);
725
			newRank =  Rank.SUBVARIETY();
726
		}else if (StringUtils.isNotBlank(epithets.varietyName)  && lowerTaxonRank.isLower(Rank.VARIETY())){
727
			taxonName.setInfraSpecificEpithet(epithets.varietyName);
728
			newRank =  Rank.VARIETY();
729
		}else if (StringUtils.isNotBlank(epithets.subspeciesName)  && lowerTaxonRank.isLower(Rank.SUBSPECIES())){
730
			taxonName.setInfraSpecificEpithet(epithets.subspeciesName);
731
			newRank = Rank.SUBSPECIES();
732
		}
733

    
734
		if (newRank != null){
735
			taxonName.setSpecificEpithet(epithets.specificEpithet);
736
			taxonName.setGenusOrUninomial(epithets.genusName);
737
			taxonName.setRank(newRank);
738
		}
739

    
740
		return taxonName;
741
	}
742

    
743
	private IBotanicalName handleSpecies(Rank lowerTaxonRank, IBotanicalName taxonName, Epithets epithets) {
744
		Rank newRank = null;
745

    
746
		if (StringUtils.isNotBlank(epithets.specificEpithet)  && lowerTaxonRank.isLower(Rank.SPECIES())){
747
			taxonName.setSpecificEpithet(epithets.specificEpithet);
748
			newRank = Rank.SPECIES();
749
		}
750
		if (newRank != null){
751
			taxonName.setGenusOrUninomial(epithets.genusName);
752
			taxonName.setRank(newRank);
753
		}
754
		return taxonName;
755
	}
756

    
757
	private IBotanicalName handleInfraGeneric(Rank lowerTaxonRank, IBotanicalName taxonName, Epithets epithets) {
758
		Rank newRank = null;
759

    
760
		if (StringUtils.isNotBlank(epithets.seriesName)  && lowerTaxonRank.isLower(Rank.SERIES())){
761
			taxonName.setInfraGenericEpithet(epithets.seriesName);
762
			newRank = Rank.SERIES();
763
		}else if (StringUtils.isNotBlank(epithets.subsectionName)  && lowerTaxonRank.isLower(Rank.SUBSECTION_BOTANY())){
764
			taxonName.setInfraGenericEpithet(epithets.subsectionName);
765
			newRank =  Rank.SUBSECTION_BOTANY();
766
		}else if (StringUtils.isNotBlank(epithets.sectionName)  && lowerTaxonRank.isLower(Rank.SECTION_BOTANY())){
767
			taxonName.setInfraGenericEpithet(epithets.sectionName);
768
			newRank =  Rank.SECTION_BOTANY();
769
		}else if (StringUtils.isNotBlank(epithets.subGenusName) && lowerTaxonRank.isLower(Rank.SUBGENUS())){
770
			taxonName.setInfraGenericEpithet(epithets.subGenusName);
771
			newRank = Rank.SUBGENUS();
772
		}
773
		if (newRank != null){
774
			taxonName.setGenusOrUninomial(epithets.genusName);
775
			taxonName.setRank(newRank);
776
		}
777
		return taxonName;
778
	}
779

    
780
	private IBotanicalName handleUninomial(Rank lowerTaxonRank, IBotanicalName taxonName,  Epithets epithets) {
781

    
782
		Rank newRank = null;
783
		if (StringUtils.isNotBlank(epithets.genusName) && lowerTaxonRank.isLower(Rank.GENUS())){
784
			taxonName.setGenusOrUninomial(epithets.genusName);
785
			newRank =  Rank.GENUS();
786
		}else if (StringUtils.isNotBlank(epithets.subTribusName) && lowerTaxonRank.isLower(Rank.SUBTRIBE())){
787
			taxonName.setGenusOrUninomial(epithets.subTribusName);
788
			newRank =  Rank.SUBTRIBE();
789
		}else if (StringUtils.isNotBlank(epithets.tribusName) && lowerTaxonRank.isLower(Rank.TRIBE())){
790
			taxonName.setGenusOrUninomial(epithets.tribusName);
791
			newRank =  Rank.TRIBE();
792
		}else if (StringUtils.isNotBlank(epithets.subFamilyName) && lowerTaxonRank.isLower(Rank.SUBFAMILY())){
793
			taxonName.setGenusOrUninomial(epithets.subFamilyName);
794
			newRank =  Rank.SUBFAMILY();
795
		}else if (StringUtils.isNotBlank(epithets.familyName) && lowerTaxonRank.isLower(Rank.FAMILY())){
796
			taxonName.setGenusOrUninomial(epithets.familyName);
797
			newRank =  Rank.FAMILY();
798
		}else if (StringUtils.isNotBlank(epithets.subOrderName) && lowerTaxonRank.isLower(Rank.SUBORDER())){
799
			taxonName.setGenusOrUninomial(epithets.subOrderName);
800
			newRank =  Rank.SUBORDER();
801
		}else if (StringUtils.isNotBlank(epithets.orderName) && lowerTaxonRank.isLower(Rank.ORDER())){
802
			taxonName.setGenusOrUninomial(epithets.orderName);
803
			newRank =  Rank.ORDER();
804
		}
805
		taxonName.setRank(newRank);
806
		return taxonName;
807
	}
808

    
809
	/**
810
	 * for internal use only, used by MethodMapper
811
	 */
812
	private TaxonBase mapCommonName(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException{
813
		String taxonNumber = state.getTaxonNumber();
814
		String commonNames = rs.getString("Common names");
815
		TaxonBase<?> taxonBase = state.getRelatedObject(state.CURRENT_OBJECT_NAMESPACE, state.CURRENT_OBJECT_ID, TaxonBase.class);
816
		if (StringUtils.isNotBlank(commonNames)){
817
			Taxon taxon = getAcceptedTaxon(taxonBase);
818
			if ( taxon != null ){
819
				TaxonDescription description = getTaxonDescription(taxon, false, true);
820
				String[] split = commonNames.split(",");
821
				for (String commonNameString: split){
822
					CommonTaxonName commonName = CommonTaxonName.NewInstance(commonNameString.trim(), Language.ENGLISH());
823
					description.addElement(commonName);
824
				}
825
			}else{
826
				logger.warn(taxonNumber + " - Accepted taxon for synonym can't be defined for common name. Synonym " + taxonBase.getName().getTitleCache());
827
			}
828
		}
829
		return taxonBase;
830
	}
831

    
832
	/**
833
	 * for internal use only, used by MethodMapper
834
	 */
835
	private TaxonBase mapDistribution(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException{
836
		try {
837
			String taxonNumber = state.getTaxonNumber();
838
//			logger.info(taxonNumber);
839
			TaxonBase<?> taxonBase = state.getRelatedObject(state.CURRENT_OBJECT_NAMESPACE, state.CURRENT_OBJECT_ID, TaxonBase.class);
840
			String countriesString = rs.getString("Distribution - Country");
841
			String province = rs.getString("Distribution - Province");
842
			String distributionDetailed = rs.getString("Distribution - detailed");
843
			if (taxonBase != null){
844
				TaxonName nameUsedInSource = taxonBase.getName();
845
				Taxon taxon = getAcceptedTaxon(taxonBase);
846
				if (taxon != null){
847

    
848
					if (StringUtils.isNotBlank(countriesString) ){
849
						makeCountries(state, taxonNumber, taxon, nameUsedInSource, countriesString, province, distributionDetailed);
850
					}
851
					makeProvince(taxon, province);
852
					makeDistributionDetailed(taxon, distributionDetailed);
853
				}else{
854
					logger.warn(taxonNumber + " - Accepted taxon for synonym can't be defined for distribution. Synonym " + taxonBase.getName().getTitleCache());
855
				}
856
			}else{
857
				logger.warn(" - " + taxonNumber + ": TaxonBase was null");
858
			}
859
			return taxonBase;
860
		} catch (Exception e) {
861
			e.printStackTrace();
862
			return null;
863
		}
864
	}
865

    
866
	/**
867
	 * for internal use only, used by MethodMapper
868
	 * @param commonNames
869
	 */
870
	private TaxonBase mapEcology(ResultSet rs, CentralAfricaFernsImportState state) throws SQLException{
871
		String taxonNumber = state.getTaxonNumber();
872
		String ecologyString = rs.getString("Ecology");
873
		TaxonBase<?> taxonBase = state.getRelatedObject(state.CURRENT_OBJECT_NAMESPACE, state.CURRENT_OBJECT_ID, TaxonBase.class);
874
		if (StringUtils.isNotBlank(ecologyString)){
875
			Taxon taxon = getAcceptedTaxon(taxonBase);
876

    
877
			if (taxon != null){
878
				TaxonDescription description = getTaxonDescription(taxon, false, true);
879
				TextData ecology = TextData.NewInstance(Feature.ECOLOGY());
880
				ecology.putText(Language.ENGLISH(), ecologyString.trim());
881
				description.addElement(ecology);
882
			}else{
883
				logger.warn(taxonNumber + " - Accepted taxon for synonym can't be defined for ecology. Synonym " + taxonBase.getName().getTitleCache());
884
			}
885
		}
886
		return taxonBase;
887
	}
888

    
889
	private void makeDistributionDetailed(Taxon taxon, String distributionDetailed) {
890
		if (StringUtils.isNotBlank(distributionDetailed)){
891
			TaxonDescription description = getTaxonDescription(taxon, false, true);
892
			TextData distribution = TextData.NewInstance(Feature.DISTRIBUTION());
893
			description.addElement(distribution);
894
			distribution.putText(Language.ENGLISH(), distributionDetailed);
895
		}
896
	}
897

    
898
	private void makeProvince(Taxon taxon, String province) {
899
		if (StringUtils.isNotBlank(province)){
900
			TaxonDescription description = getTaxonDescription(taxon, false, true);
901
			TextData distribution = TextData.NewInstance(Feature.DISTRIBUTION());
902
			description.addElement(distribution);
903
			distribution.putText(Language.ENGLISH(), province);
904
		}
905
	}
906

    
907
	private void makeCountries(CentralAfricaFernsImportState state, String taxonNumber, Taxon taxon, TaxonName nameUsedInSource, String countriesString, String province, String distributionDetailed) {
908
		countriesString = countriesString.replaceAll("\\*", "");
909
		countriesString = countriesString.replace("  ", " ");
910
		countriesString = countriesString.replace(", endemic", " - endemic");
911
		countriesString = countriesString.replace("(endemic)", " - endemic");
912
		countriesString = countriesString.replace("(introduced)", " - introduced");
913
		countriesString = countriesString.replace("(naturalised)", " - naturalised");
914
		countriesString = countriesString.replace("Madagascar-", "Madagascar -");
915
		countriesString = countriesString.replace("Mah\u00e9", "Mahe");
916

    
917
		String[] split = countriesString.split("[,;]");
918
		String remainingString = null;
919
		for (String countryString : split){
920
			countryString = CdmUtils.concat(", ", remainingString , countryString);
921
			if (countryString.matches(".*\\(.*") && ! countryString.matches(".*\\).*")){
922
				remainingString = countryString;
923
				continue;
924
			}
925
			remainingString = null;
926
			try {
927
				makeSingleCountry(state, taxonNumber, taxon, nameUsedInSource, countryString.trim());
928
			} catch (UndefinedTransformerMethodException e) {
929
				e.printStackTrace();
930
			}
931
		}
932
	}
933

    
934
	private void makeSingleCountry(CentralAfricaFernsImportState state, String taxonNumber, Taxon taxon, TaxonName nameUsedInSource, String country) throws UndefinedTransformerMethodException {
935
		boolean areaDoubtful = false;
936
		Distribution distribution = Distribution.NewInstance(null, PresenceAbsenceTerm.PRESENT());
937
		Reference sourceReference = this.sourceReference;
938
		distribution.addSource(OriginalSourceType.Import, taxonNumber, "Distribution_Country", sourceReference, null, nameUsedInSource, null);
939
		NamedArea area = null;
940
		//empty
941
		if (StringUtils.isBlank(country)){
942
			return;
943
		}
944
		country = country.trim();
945
		//doubtful
946
		if (country.startsWith("?")){
947
			areaDoubtful = true;
948
			country = country.substring(1).trim();
949
		}
950
		//status
951
		country = makeCountryStatus(state, country, distribution);
952

    
953
		//brackets
954
		country = makeCountryBrackets(state, taxonNumber, taxon, nameUsedInSource, country);
955
		String countryWithoutIslands = null;
956
		String countryWithoutDot = null;
957
		if (country.endsWith(" Isl.") || country.endsWith(" isl.") ){
958
			countryWithoutIslands = country.substring(0, country.length()-5);
959
		}
960
		if (country.endsWith(".")){
961
			countryWithoutDot = country.substring(0, country.length()-1);
962
		}
963
		if (country.endsWith("*")){
964
			country = country.substring(0, country.length()-1);
965
		}
966
		if (country.endsWith("Islands")){
967
			country = country.replace("Islands", "Is.");
968
		}
969

    
970
		//areas
971
		if (TdwgAreaProvider.isTdwgAreaLabel(country)){
972
			//tdwg
973
			area = TdwgAreaProvider.getAreaByTdwgLabel(country);
974
		}else if (TdwgAreaProvider.isTdwgAreaLabel(countryWithoutIslands)){
975
			//tdwg
976
			area = TdwgAreaProvider.getAreaByTdwgLabel(countryWithoutIslands);
977
		}else if (TdwgAreaProvider.isTdwgAreaLabel(countryWithoutDot)){
978
			//tdwg
979
			area = TdwgAreaProvider.getAreaByTdwgLabel(countryWithoutDot);
980
		}else if ( (area = state.getTransformer().getNamedAreaByKey(country)) != null) {
981
			//area already set
982
		}else if (Country.isCountryLabel(country)){
983
			//iso
984
			area = Country.getCountryByLabel(country);
985
		}else{
986
			//others
987
			NamedAreaLevel level = null;
988
			NamedAreaType areaType = null;
989

    
990
			UUID uuid = state.getTransformer().getNamedAreaUuid(country);
991
			if (uuid == null){
992
				logger.error(taxonNumber + " - Unknown country: " + country);
993
			}
994
			area = getNamedArea(state, uuid, country, country, country, areaType, level);
995
		}
996

    
997
		distribution.setArea(area);
998
		if (areaDoubtful == true){
999
			if (distribution.getStatus().equals(PresenceAbsenceTerm.PRESENT())){
1000
				distribution.setStatus(PresenceAbsenceTerm.PRESENT_DOUBTFULLY());
1001
			}
1002
		}
1003
		TaxonDescription description = getTaxonDescription(taxon, false, true);
1004
		description.addElement(distribution);
1005
	}
1006

    
1007
	private Reference sourceReference = null;
1008
	private Reference getFernsSourceReference(CentralAfricaFernsImportState state) {
1009
//		if (sourceReference == null || true){
1010
			Reference tmpReference = state.getConfig().getSourceReference();
1011
			sourceReference = getReferenceService().find(tmpReference.getUuid());
1012
//		}
1013
		return sourceReference;
1014
	}
1015

    
1016
	private String makeCountryBrackets(CentralAfricaFernsImportState state, String taxonNumber, Taxon taxon,
1017
	        TaxonName nameUsedInSource, String country) {
1018
		String[] split = (country + " ").split("\\(.*\\)");
1019
		if (split.length == 2){
1020
			String bracket = country.substring(split[0].length()+1, country.indexOf(")"));
1021
			country = split[0].trim();
1022
			makeCountries(state, taxonNumber, taxon, nameUsedInSource, bracket, null, null);
1023
		}else if (split.length ==1){
1024
			//do nothing
1025
		}else{
1026
			logger.warn("Illegal length");
1027
		}
1028
		return country;
1029
	}
1030

    
1031
	private String makeCountryStatus(CentralAfricaFernsImportState state, String country, Distribution distribution) throws UndefinedTransformerMethodException {
1032
		PresenceAbsenceTerm status = null;
1033
		String[] split = country.split(" - ");
1034

    
1035
		if (split.length == 2){
1036
			country = split[0].trim();
1037
			String statusString = split[1];
1038
			statusString = statusString.replace(".", "");
1039
			status = state.getTransformer().getPresenceTermByKey(statusString);
1040
			if (status == null){
1041
				logger.warn("No status found: "+  statusString);
1042
			}
1043
//			UUID uuid = null;
1044
//			status = getPresenceTerm(state, uuid, statusString, statusString, null);
1045
		}else if (split.length == 1){
1046
			//nothing to do
1047
		}else{
1048
			logger.warn("Invalid length: " + split.length);
1049
		}
1050
		if (status != null){
1051
			distribution.setStatus(status);
1052
		}
1053
		return country;
1054
	}
1055

    
1056
	@Override
1057
	protected boolean doCheck(CentralAfricaFernsImportState state){
1058
		IOValidator<CentralAfricaFernsImportState> validator = new CentralAfricaFernsTaxonImportValidator();
1059
		return validator.validate(state);
1060
	}
1061

    
1062
	@Override
1063
	protected boolean isIgnore(CentralAfricaFernsImportState state){
1064
		return ! state.getConfig().isDoRelTaxa();
1065
	}
1066

    
1067
//************************ OLD **********************************************************
1068

    
1069
	/**
1070
	 * Adds the higherTaxon authors to the existingHigherTaxon authors if the higherTaxon has authors and
1071
	 * the existingHigherTaxon has no authors.
1072
	 * Returns false if both taxa have authors and the authors differ from each other.
1073
	 * @param higherTaxon
1074
	 * @param existingHigherTaxon
1075
	 */
1076
	private boolean mergeAuthors_old(Taxon higherTaxon, Taxon existingHigherTaxon) {
1077
		INonViralName existingName = higherTaxon.getName();
1078
		INonViralName newName = existingHigherTaxon.getName();
1079
		if (existingName == newName){
1080
			return true;
1081
		}
1082
		if (! newName.hasAuthors()){
1083
			return true;
1084
		}
1085
		if (! existingName.hasAuthors()){
1086
			existingName.setCombinationAuthorship(newName.getCombinationAuthorship());
1087
			existingName.setExCombinationAuthorship(newName.getExCombinationAuthorship());
1088
			existingName.setBasionymAuthorship(newName.getBasionymAuthorship());
1089
			existingName.setExBasionymAuthorship(newName.getExBasionymAuthorship());
1090
			return true;
1091
		}
1092
		boolean authorsAreSame = true;
1093
		authorsAreSame &= getNomTitleNz(existingName.getCombinationAuthorship()).equals(getNomTitleNz(newName.getCombinationAuthorship()));
1094
		authorsAreSame &= getNomTitleNz(existingName.getExCombinationAuthorship()).equals(getNomTitleNz(newName.getExCombinationAuthorship()));
1095
		authorsAreSame &= getNomTitleNz(existingName.getBasionymAuthorship()).equals(getNomTitleNz(newName.getBasionymAuthorship()));
1096
		authorsAreSame &= getNomTitleNz(existingName.getExBasionymAuthorship()).equals(getNomTitleNz(newName.getExBasionymAuthorship()));
1097
		return authorsAreSame;
1098

    
1099

    
1100
	}
1101

    
1102
	/**
1103
	 * Returns the nomenclatural title of the author. Returns empty string if author is <code>null</code> or
1104
	 * titleCache is <code>null</code>.
1105
	 * @param author
1106
	 * @return
1107
	 */
1108
	private String getNomTitleNz(INomenclaturalAuthor author) {
1109
		if (author != null){
1110
			return CdmUtils.Nz(author.getNomenclaturalTitle());
1111
		}else{
1112
			return "";
1113
		}
1114
	}
1115

    
1116
	private Taxon getExistingHigherTaxon_old(Taxon child, Taxon higherTaxon) {
1117
		int countNodes = child.getTaxonNodes().size();
1118
		if (countNodes < 1){
1119
			return null;
1120
		}else if (countNodes > 1){
1121
			throw new IllegalStateException("Multiple nodes exist for child taxon. This is an invalid state.");
1122
		}else{
1123
			TaxonNode childNode = child.getTaxonNodes().iterator().next();
1124
			TaxonNode parentNode = childNode.getParent();
1125
			if (parentNode != null){
1126
				String existingParentTitle = parentNode.getTaxon().getName().getTitleCache();
1127
				String newParentTitle = higherTaxon.getName().getTitleCache();
1128
				if (existingParentTitle.equals(newParentTitle)){
1129
					return parentNode.getTaxon();
1130
				}
1131
			}
1132
			return null;
1133
		}
1134
	}
1135

    
1136
	/**
1137
	 * Tests if this the child taxon already is a child of the higher taxon.
1138
	 */
1139
	private boolean includedRelationshipExists_Old(Taxon child, Taxon higherTaxon) {
1140
		int countNodes = higherTaxon.getTaxonNodes().size();
1141
		if (countNodes < 1){
1142
			return false;
1143
		}else if (countNodes > 1){
1144
			throw new IllegalStateException("Multiple nodes exist for higher taxon. This is an invalid state.");
1145
		}else{
1146
			TaxonNode higherNode = higherTaxon.getTaxonNodes().iterator().next();
1147
			return childExists_old(child, higherNode);
1148
		}
1149
	}
1150

    
1151
	private boolean childExists_old(Taxon child, TaxonNode higherNode) {
1152
		for (TaxonNode childNode : higherNode.getChildNodes()){
1153
			String existingChildTitle = childNode.getTaxon().getName().getTitleCache();
1154
			String newChildTitle = child.getName().getTitleCache();
1155
			if (existingChildTitle.equals(newChildTitle)){
1156
				return true;
1157
			}
1158
		}
1159
		return false;
1160
	}
1161
}
(6-6/7)