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.logging.log4j.LogManager;
23
import org.apache.logging.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.TeamOrPersonBase;
37
import eu.etaxonomy.cdm.model.common.CdmBase;
38
import eu.etaxonomy.cdm.model.common.Language;
39
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
40
import eu.etaxonomy.cdm.model.description.Distribution;
41
import eu.etaxonomy.cdm.model.description.Feature;
42
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
43
import eu.etaxonomy.cdm.model.description.TaxonDescription;
44
import eu.etaxonomy.cdm.model.description.TextData;
45
import eu.etaxonomy.cdm.model.location.Country;
46
import eu.etaxonomy.cdm.model.location.NamedArea;
47
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
48
import eu.etaxonomy.cdm.model.location.NamedAreaType;
49
import eu.etaxonomy.cdm.model.name.IBotanicalName;
50
import eu.etaxonomy.cdm.model.name.INonViralName;
51
import eu.etaxonomy.cdm.model.name.Rank;
52
import eu.etaxonomy.cdm.model.name.TaxonName;
53
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
54
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
55
import eu.etaxonomy.cdm.model.reference.Reference;
56
import eu.etaxonomy.cdm.model.taxon.Classification;
57
import eu.etaxonomy.cdm.model.taxon.Synonym;
58
import eu.etaxonomy.cdm.model.taxon.SynonymType;
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
 * @author a.mueller
65
 */
66
@Component
67
public class CentralAfricaFernsTaxonRelationImport
68
        extends CentralAfricaFernsImportBase<TaxonBase>
69
        implements IMappingImport<TaxonBase, CentralAfricaFernsImportState>{
70

    
71
    private static final long serialVersionUID = 5561099127416844593L;
72
    private static Logger logger = LogManager.getLogger();
73

    
74
	private DbImportMapping<?,?> mapping;
75

    
76
	private static final String pluralString = "taxon relations";
77
	private static final String dbTableName = "[African pteridophytes]";
78
	private static final Class<?> cdmTargetClass = TaxonBase.class;
79

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

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

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

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

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

    
103
		}
104
		return mapping;
105
	}
106

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
252
		return synonym;
253
	}
254

    
255

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

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

    
290

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

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

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

    
316

    
317

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

    
358

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

    
379

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

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

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

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

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

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

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

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

    
519

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

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

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

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

    
565

    
566

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

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

    
585

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

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

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

    
634
		return constructedHigherTaxon;
635
	}
636

    
637

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

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

    
677

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

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

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

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

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

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

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

    
741
		return taxonName;
742
	}
743

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1068
//************************ OLD **********************************************************
1069

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

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

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

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

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