Project

General

Profile

Download (48.3 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.PresenceAbsenceTermBase;
44
import eu.etaxonomy.cdm.model.description.PresenceTerm;
45
import eu.etaxonomy.cdm.model.description.TaxonDescription;
46
import eu.etaxonomy.cdm.model.description.TextData;
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.location.Country;
51
import eu.etaxonomy.cdm.model.name.BotanicalName;
52
import eu.etaxonomy.cdm.model.name.NonViralName;
53
import eu.etaxonomy.cdm.model.name.Rank;
54
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
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.SynonymRelationship;
59
import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;
60
import eu.etaxonomy.cdm.model.taxon.Taxon;
61
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
62
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
63

    
64

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

    
69
@Component
70
public class CentralAfricaFernsTaxonRelationImport  extends CentralAfricaFernsImportBase<TaxonBase> implements IMappingImport<TaxonBase, CentralAfricaFernsImportState>{
71
	private static final Logger logger = Logger.getLogger(CentralAfricaFernsTaxonRelationImport.class);
72
	
73
	private DbImportMapping<?,?> mapping;
74
	
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 Map<String, UUID> nameCacheTaxonMap = new HashMap<String, UUID>();
81
	private Map<String, UUID> titleCacheTaxonMap = new HashMap<String, UUID>();
82

    
83
	private CentralAfricaFernsImportState state;
84

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

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

    
101

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

    
115
		}
116
		return mapping;
117
	}
118

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

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

    
144

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

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

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

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

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

    
227
	}
228

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

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

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

    
330

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

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

    
356

    
357

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

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

    
430

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

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

    
470

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

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

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

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

    
558

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

    
581

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

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

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

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

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

    
718

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

    
765

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

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

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

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

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

    
869

    
870

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

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

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

    
983

    
984

    
985

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

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

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

    
1039

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

    
1114

    
1115

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

    
1129

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

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

    
1169

    
1170
	
1171

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

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

    
1189
	
1190
	
1191
//************************ OLD **********************************************************
1192

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

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

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

    
1260

    
1261

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

    
1280

    
1281

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

    
1293

    
1294
}
(6-6/7)