Project

General

Profile

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

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

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

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

    
26
import eu.etaxonomy.cdm.api.service.IClassificationService;
27
import eu.etaxonomy.cdm.common.CdmUtils;
28
import eu.etaxonomy.cdm.io.common.IOValidator;
29
import eu.etaxonomy.cdm.io.common.TdwgAreaProvider;
30
import eu.etaxonomy.cdm.io.common.mapping.DbImportMapping;
31
import eu.etaxonomy.cdm.io.common.mapping.DbImportMethodMapper;
32
import eu.etaxonomy.cdm.io.common.mapping.DbImportTaxIncludedInMapper;
33
import eu.etaxonomy.cdm.io.common.mapping.IMappingImport;
34
import eu.etaxonomy.cdm.io.common.mapping.UndefinedTransformerMethodException;
35
import eu.etaxonomy.cdm.io.eflora.centralAfrica.ferns.validation.CentralAfricaFernsTaxonImportValidator;
36
import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
37
import eu.etaxonomy.cdm.model.common.CdmBase;
38
import eu.etaxonomy.cdm.model.common.Language;
39
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
40
import eu.etaxonomy.cdm.model.description.CommonTaxonName;
41
import eu.etaxonomy.cdm.model.description.Distribution;
42
import eu.etaxonomy.cdm.model.description.Feature;
43
import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
44
import eu.etaxonomy.cdm.model.description.TaxonDescription;
45
import eu.etaxonomy.cdm.model.description.TextData;
46
import eu.etaxonomy.cdm.model.location.Country;
47
import eu.etaxonomy.cdm.model.location.NamedArea;
48
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
49
import eu.etaxonomy.cdm.model.location.NamedAreaType;
50
import eu.etaxonomy.cdm.model.name.BotanicalName;
51
import eu.etaxonomy.cdm.model.name.NonViralName;
52
import eu.etaxonomy.cdm.model.name.Rank;
53
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
54
import eu.etaxonomy.cdm.model.reference.Reference;
55
import eu.etaxonomy.cdm.model.taxon.Classification;
56
import eu.etaxonomy.cdm.model.taxon.Synonym;
57
import eu.etaxonomy.cdm.model.taxon.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
/**
64
 * @author a.mueller
65
 */
66

    
67
@Component
68
public class CentralAfricaFernsTaxonRelationImport  extends CentralAfricaFernsImportBase<TaxonBase> implements IMappingImport<TaxonBase, CentralAfricaFernsImportState>{
69
	private static final Logger logger = Logger.getLogger(CentralAfricaFernsTaxonRelationImport.class);
70

    
71
	private DbImportMapping<?,?> mapping;
72

    
73

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

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

    
81
	private CentralAfricaFernsImportState state;
82

    
83

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

    
88

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

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

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

    
105
		}
106
		return mapping;
107
	}
108

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

    
122

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

    
134

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

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

    
154

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

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

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

    
177

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

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

    
191

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

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

    
216
	}
217

    
218

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

    
242

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

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

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

    
277
		return synonym;
278
	}
279

    
280

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

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

    
319

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

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

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

    
345

    
346

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

    
395

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

    
419

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

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

    
459

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

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

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

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

    
547

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

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

    
570

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

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

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

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

    
628

    
629

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

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

    
654

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

    
680

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

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

    
704
		return constructedHigherTaxon;
705
	}
706

    
707

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

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

    
754

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

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

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

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

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

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

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

    
818
		return taxonName;
819
	}
820

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

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

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

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

    
858

    
859

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

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

    
889

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

    
913

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

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

    
948

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

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

    
972

    
973

    
974

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

    
984

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

    
994

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

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

    
1028

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

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

    
1065

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

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

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

    
1103

    
1104

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

    
1118

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

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

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

    
1158

    
1159

    
1160

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

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

    
1178

    
1179

    
1180
//************************ OLD **********************************************************
1181

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

    
1212

    
1213
	}
1214

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

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

    
1249

    
1250

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

    
1269

    
1270

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

    
1282

    
1283
}
(6-6/7)