Project

General

Profile

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

    
11
import java.util.ArrayList;
12
import java.util.EnumSet;
13
import java.util.HashSet;
14
import java.util.Iterator;
15
import java.util.List;
16
import java.util.Set;
17
import java.util.UUID;
18

    
19
import org.apache.log4j.Logger;
20

    
21
import eu.etaxonomy.cdm.api.service.pager.Pager;
22
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
23
import eu.etaxonomy.cdm.io.common.DbExportBase;
24
import eu.etaxonomy.cdm.io.common.mapping.out.DbLastActionMapper;
25
import eu.etaxonomy.cdm.io.pesi.erms.ErmsTransformer;
26
import eu.etaxonomy.cdm.model.common.AnnotationType;
27
import eu.etaxonomy.cdm.model.common.CdmBase;
28
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
29
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
30
import eu.etaxonomy.cdm.model.common.Marker;
31
import eu.etaxonomy.cdm.model.common.MarkerType;
32
import eu.etaxonomy.cdm.model.common.RelationshipBase;
33
import eu.etaxonomy.cdm.model.description.Feature;
34
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
35
import eu.etaxonomy.cdm.model.description.TextData;
36
import eu.etaxonomy.cdm.model.name.HybridRelationship;
37
import eu.etaxonomy.cdm.model.name.INonViralName;
38
import eu.etaxonomy.cdm.model.name.NameRelationship;
39
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
40
import eu.etaxonomy.cdm.model.name.NomenclaturalSource;
41
import eu.etaxonomy.cdm.model.name.TaxonName;
42
import eu.etaxonomy.cdm.model.reference.Reference;
43
import eu.etaxonomy.cdm.model.taxon.Synonym;
44
import eu.etaxonomy.cdm.model.taxon.Taxon;
45
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
46
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
47
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
48
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
49
import eu.etaxonomy.cdm.persistence.query.OrderHint;
50
import eu.etaxonomy.cdm.strategy.cache.name.TaxonNameDefaultCacheStrategy;
51
import eu.etaxonomy.cdm.strategy.cache.name.ZooNameNoMarkerCacheStrategy;
52

    
53
/**
54
 * @author e.-m.lee
55
 * @since 12.02.2010
56
 */
57
public abstract class PesiExportBase
58
              extends DbExportBase<PesiExportConfigurator, PesiExportState, PesiTransformer> {
59

    
60
    private static final long serialVersionUID = 6226747017958138156L;
61
    private static final Logger logger = Logger.getLogger(PesiExportBase.class);
62

    
63
	protected static final boolean IS_CACHE = true;
64

    
65
	private static Set<NameRelationshipType> excludedRelTypes = new HashSet<>();
66

    
67
	private static TaxonNameDefaultCacheStrategy zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
68
	private static TaxonNameDefaultCacheStrategy botanicalNameStrategy = TaxonNameDefaultCacheStrategy.NewInstance();
69

    
70
	public PesiExportBase() {
71
		super();
72
	}
73

    
74
	protected <CLASS extends TaxonBase> List<CLASS> getNextTaxonPartition(Class<CLASS> clazz, int limit,
75
	        int partitionCount, List<String> propertyPath) {
76

    
77
	    List<OrderHint> orderHints = new ArrayList<>();
78
		orderHints.add(new OrderHint("id", OrderHint.SortOrder.ASCENDING ));
79

    
80
		List<CLASS> list = getTaxonService().list(clazz, limit, partitionCount * limit, orderHints, propertyPath);
81

    
82
		if (list.isEmpty()){
83
			return null;
84
		}
85

    
86
		Iterator<CLASS> it = list.iterator();
87
		while (it.hasNext()){
88
			TaxonBase<?> taxonBase = it.next();
89
			if (! isPesiTaxon(taxonBase)){
90
				it.remove();
91
			}
92
			taxonBase = null;
93
		}
94
		it = null;
95
		return list;
96
	}
97

    
98
	protected List<TaxonNameDescription> getNextNameDescriptionPartition(int limit, int partitionCount, List<String> propertyPath) {
99
		List<OrderHint> orderHints = new ArrayList<>();
100
		orderHints.add(new OrderHint("id", OrderHint.SortOrder.ASCENDING ));
101
		Pager<TaxonNameDescription> l = getDescriptionService().getTaxonNameDescriptions(null, limit, partitionCount, propertyPath);
102
		List<TaxonNameDescription> list = l.getRecords();
103
		if (list.isEmpty()){
104
			return null;
105
		}
106

    
107
		Iterator<TaxonNameDescription> it = list.iterator();
108
		while (it.hasNext()){
109
			TaxonNameDescription nameDescription = it.next();
110
			if (! isPesiNameDescriptionTaxon(nameDescription)){
111
				it.remove();
112
			}
113
		}
114
		return list;
115
	}
116

    
117
	private boolean isPesiNameDescriptionTaxon(TaxonNameDescription nameDescription) {
118
		TaxonName name = nameDescription.getTaxonName();
119
		if (isPurePesiName(name)){
120
			return true;
121
		}else{
122
			Set<TaxonBase> taxa = name.getTaxonBases();
123
			for (TaxonBase<?> taxonBase : taxa){
124
				if (isPesiTaxon(taxonBase)){
125
					return true;
126
				}
127
			}
128
		}
129
		return false;
130
	}
131

    
132
	/**
133
	 * Returns the next list of pure names. If finished result will be null. If list is empty there may be result in further partitions.
134
	 */
135
	protected List<TaxonName> getNextPureNamePartition(Class<TaxonName> clazz,int limit, int partitionCount) {
136
		List<OrderHint> orderHints = new ArrayList<>();
137
		orderHints.add(new OrderHint("id", OrderHint.SortOrder.ASCENDING ));
138
//		List<String> propPath = Arrays.asList(new String[]{"taxonBases"});
139

    
140
		List<TaxonName> list = getNameService().list(clazz, limit, partitionCount * limit, orderHints, null);
141
		if (list.isEmpty()){
142
			return null;
143
		}
144
		Iterator<TaxonName> it = list.iterator();
145
		while (it.hasNext()){
146
		    TaxonName taxonName = HibernateProxyHelper.deproxy(it.next());
147
			if (! isPurePesiName(taxonName)){
148
				it.remove();
149
			}
150
		}
151
		return list;
152
	}
153

    
154
	protected List<NomenclaturalSource> getNextOriginalSpellingPartition(
155
	        int pageSize, int partitionCount, List<String> propertyPaths) {
156

    
157
       List<NomenclaturalSource> result = new ArrayList<>();
158
       List<OrderHint> orderHints = null;
159
       List<NomenclaturalSource> list;
160
       list = getNameService().listOriginalSpellings(pageSize, partitionCount, orderHints, propertyPaths);
161

    
162
       if (list.isEmpty()){
163
           return null;
164
       }
165
       for (NomenclaturalSource rel : list){
166
           if (isPesiOriginalSpelling(rel)){
167
               result.add(rel);
168
           }
169
       }
170
       return result;
171
    }
172

    
173
	protected <CLASS extends RelationshipBase> List<CLASS> getNextNameRelationshipPartition(
174
	                Class<CLASS> clazz, int pageSize, int partitionCount, List<String> propertyPaths) {
175

    
176
	    List<CLASS> result = new ArrayList<>();
177
		List<OrderHint> orderHints = null;
178
		List<CLASS> list;
179
		if (NameRelationship.class.isAssignableFrom(clazz)){
180
            list = (List<CLASS>)getNameService().listNameRelationships(null, pageSize, partitionCount, orderHints, propertyPaths);
181
        }else if (HybridRelationship.class.isAssignableFrom(clazz)){
182
            list = (List<CLASS>)getNameService().listHybridRelationships(null, pageSize, partitionCount, orderHints, propertyPaths);
183
        }else{
184
            throw new RuntimeException("Only NameRelationship or HybridRelationship allowed here");
185
        }
186
		if (list.isEmpty()){
187
			return null;
188
		}
189
		for (CLASS rel : list){
190
			if (isPesiNameRelationship(rel)){
191
				result.add(rel);
192
			}
193
		}
194
		return result;
195
	}
196

    
197
	protected <CLASS extends RelationshipBase> List<CLASS> getNextTaxonRelationshipPartition( int limit, int partitionCount, List<String> propertyPaths) {
198

    
199
	    List<CLASS> result = new ArrayList<>();
200

    
201
	    List<OrderHint> orderHints = null;
202
		@SuppressWarnings("unchecked")
203
        List<CLASS> list = (List<CLASS>)this.getTaxonService()
204
		        .listTaxonRelationships(null, limit, partitionCount, orderHints, propertyPaths);
205

    
206
		if (list.isEmpty()){
207
			return null;
208
		}
209

    
210
		for (CLASS rel : list){
211
			if (isPesiTaxonOrSynonymRelationship(rel)){
212
				result.add(rel);
213
			}
214
		}
215
		return result;
216
	}
217

    
218
    protected List<TaxonNode> getNextTaxonNodePartition( int limit, int partitionCount, List<String> propertyPaths) {
219

    
220
        List<TaxonNode> result = new ArrayList<>();
221

    
222
        List<OrderHint> orderHints = null;
223
        List<TaxonNode> list = this.getTaxonNodeService()
224
            .list(TaxonNode.class, limit, limit * partitionCount, orderHints, propertyPaths);
225

    
226
        if (list.isEmpty()){
227
            return null;
228
        }
229

    
230
        for (TaxonNode tn : list){
231
            if (isPesiTaxonNode(tn)){
232
                result.add(tn);
233
            }
234
        }
235
        return result;
236
    }
237

    
238
    protected boolean isPesiTaxonNode(TaxonNode tn){
239
        TaxonBase<?> fromTaxon;
240
        Taxon toTaxon;
241

    
242
        fromTaxon = tn.getTaxon();
243
        toTaxon = tn.getParent()== null? null: tn.getParent().getTaxon();
244

    
245
        return (isPesiTaxon(fromTaxon, true) && isPesiTaxon(toTaxon, true));
246
    }
247

    
248
	protected boolean isPesiNameRelationship(RelationshipBase<?,?,?> rel){
249
		TaxonName name1;
250
		TaxonName name2;
251
		if (rel.isInstanceOf(HybridRelationship.class)){
252
			HybridRelationship hybridRel = CdmBase.deproxy(rel, HybridRelationship.class);
253
			name1 = hybridRel.getParentName();
254
			name2 = hybridRel.getHybridName();
255
		}else if (rel.isInstanceOf(NameRelationship.class)){
256
			NameRelationship nameRel = CdmBase.deproxy(rel, NameRelationship.class);
257
			name1 = nameRel.getFromName();
258
			name2 = nameRel.getToName();
259
		}else{
260
			logger.warn ("Only hybrid- and name-relationships alowed here");
261
			return false;
262
		}
263
		return (isPesiName(name1) && isPesiName(name2));
264
	}
265

    
266
	protected boolean isPesiOriginalSpelling(NomenclaturalSource nomSource){
267
        TaxonName name1 = nomSource.getNameUsedInSource();
268
        TaxonName name2 = nomSource.getSourcedName();
269
        return (isPesiName(name1) && isPesiName(name2));
270
    }
271

    
272
	private boolean isPesiName(TaxonName name) {
273
		return hasPesiTaxon(name) || isPurePesiName(name);
274
	}
275

    
276
	protected boolean isPesiTaxonOrSynonymRelationship(RelationshipBase rel){
277
		TaxonBase<?> fromTaxon;
278
		Taxon toTaxon;
279
		// TODO:fix!!!
280
//		if (rel.isInstanceOf(SynonymRelationship.class)){
281
//			SynonymRelationship synRel = CdmBase.deproxy(rel, SynonymRelationship.class);
282
//			fromTaxon = synRel.getSynonym();
283
//			toTaxon = synRel.getAcceptedTaxon();
284
//			synRel = null;
285
//		}else
286
		if (rel.isInstanceOf(TaxonRelationship.class)){
287
			TaxonRelationship taxRel = CdmBase.deproxy(rel, TaxonRelationship.class);
288
			fromTaxon = taxRel.getFromTaxon();
289
			toTaxon = taxRel.getToTaxon();
290
			taxRel = null;
291
		}else{
292
			logger.warn ("Only synonym - and taxon-relationships allowed here");
293
			return false;
294
		}
295
		return (isPesiTaxon(fromTaxon, false) && isPesiTaxon(toTaxon, true));
296
	}
297

    
298
	/**
299
	 * Decides if a name is not used as the name part of a PESI taxon (and therefore is
300
	 * exported to PESI as taxon already) but is related to a name used as a PESI taxon
301
	 * (e.g. as basionym, orthographic variant, etc.) and therefore should be exported
302
	 * to PESI as part of the name relationship.
303
	 * @param taxonName
304
	 * @return
305
	 */
306
	protected boolean isPurePesiName(TaxonName taxonName){
307
		if (hasPesiTaxon(taxonName)){
308
			return false;
309
		}
310

    
311
		//from names
312
		for (NameRelationship rel :taxonName.getRelationsFromThisName()){
313
			TaxonName relatedName = rel.getToName();
314
			if (hasPesiTaxon(relatedName)){
315
				return true;
316
			}
317
		}
318

    
319
		//excluded relationships on to-side
320
		initExcludedRelTypes();
321

    
322
		//to names
323
		for (NameRelationship rel :taxonName.getRelationsToThisName()){
324
			//exclude certain types
325
			if (excludedRelTypes.contains(rel.getType())){
326
				continue;
327
			}
328
			TaxonName relatedName = rel.getFromName();
329
			if (hasPesiTaxon(relatedName)){
330
				return true;
331
			}
332
		}
333

    
334
		//include hybrid parents, but no childs
335

    
336
		for (HybridRelationship rel : taxonName.getHybridParentRelations()){
337
			INonViralName child = rel.getHybridName();
338
			if (hasPesiTaxon(child)){
339
				return true;
340
			}
341
		}
342

    
343
		return false;
344
	}
345

    
346

    
347
	private void initExcludedRelTypes() {
348
		if (excludedRelTypes.isEmpty()){
349
			excludedRelTypes.add(NameRelationshipType.BASIONYM());
350
			excludedRelTypes.add(NameRelationshipType.REPLACED_SYNONYM());
351
			excludedRelTypes.add(NameRelationshipType.ORTHOGRAPHIC_VARIANT());
352
		}
353
	}
354

    
355

    
356
	/**
357
	 * Decides if a given name has "PESI taxa" attached.
358
	 *
359
	 * @see #getPesiTaxa(TaxonNameBase)
360
	 * @see #isPesiTaxon(TaxonBase)
361
	 * @param taxonName
362
	 * @return
363
	 */
364
	protected boolean hasPesiTaxon(INonViralName taxonName) {
365
		for (TaxonBase<?> taxon : taxonName.getTaxonBases()){
366
			if (isPesiTaxon(taxon)){
367
				return true;
368
			}
369
		}
370
		return false;
371
	}
372

    
373
	/**
374
	 * Returns those concepts (taxon bases) for the given name that
375
	 * are pesi taxa.
376
	 *
377
	 *  @see #isPesiTaxon(TaxonBase)
378
	 * @param name
379
	 * @return
380
	 */
381
	protected Set<TaxonBase<?>> getPesiTaxa(TaxonName name){
382
		Set<TaxonBase<?>> result = new HashSet<>();
383
		for (TaxonBase<?> taxonBase : name.getTaxonBases()){
384
			if (isPesiTaxon(taxonBase)){
385
				result.add(taxonBase);
386
			}
387
		}
388
		return result;
389
	}
390

    
391
	/**
392
	 * @see #isPesiTaxon(TaxonBase, boolean)
393
	 * @return
394
	 */
395
	protected static boolean isPesiTaxon(TaxonBase taxonBase) {
396
		return isPesiTaxon(taxonBase, false);
397
	}
398

    
399
	/**
400
	 * Checks if this taxon base is a taxon that is to be exported to PESI. This is generally the case
401
	 * but not for taxa that are marked as "unpublish". Synonyms and misapplied names are exported if they are
402
	 * related at least to one accepted taxon that is also exported, except for those misapplied names
403
	 * marked as misapplied names created by Euro+Med common names ({@linkplain https://dev.e-taxonomy.eu/redmine/issues/2786} ).
404
	 * The list of conditions may change in future.
405
	 * @param taxonBase
406
	 * @return
407
	 */
408
	protected static boolean isPesiTaxon(TaxonBase<?> taxonBase, boolean excludeMisappliedNames) {
409
		if (taxonBase == null){
410
		    return false;
411
		}
412
	    //handle accepted taxa
413
		if (taxonBase.isInstanceOf(Taxon.class)){
414
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
415
			return isPesiAcceptedTaxon(excludeMisappliedNames, taxon);
416
		//handle synonyms
417
		}else if (taxonBase.isInstanceOf(Synonym.class)){
418
			Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
419
			return isPesiSynonym(synonym);
420
		}else {
421
			throw new RuntimeException("Unknown taxon base type: " + taxonBase.getClass());
422
		}
423
	}
424

    
425
    private static boolean isPesiSynonym(Synonym synonym) {
426
        boolean hasAcceptedPesiTaxon = false;
427

    
428
        hasAcceptedPesiTaxon = isPesiTaxon(synonym.getAcceptedTaxon());
429
        if (!hasAcceptedPesiTaxon) {if (logger.isDebugEnabled()){logger.debug("Synonym has no accepted PESI taxon: " +  synonym.getUuid() + ", (" +  synonym.getTitleCache() + ")");}}
430

    
431
        return hasAcceptedPesiTaxon && synonym.isPublish();
432
    }
433

    
434
    private static boolean isPesiAcceptedTaxon(boolean excludeMisappliedNames, Taxon taxon) {
435
        if (! taxon.isPublish()){
436
        	return false;
437
        }
438

    
439
        //handle PESI accepted taxa
440
        if (! taxon.isMisapplication()){
441
        	for (Marker marker : taxon.getMarkers()){
442
        		if (marker.getValue() == false && marker.getMarkerType().equals(MarkerType.PUBLISH())){
443
        			return false;
444
        		}
445
        	}
446
        	return true;
447
        //handle misapplied names
448
        }else{
449
        	if (excludeMisappliedNames){
450
        		return false;
451
        	}
452
        	for (TaxonRelationship taxRel : taxon.getRelationsFromThisTaxon()){
453
        		if (taxRel.getType().isAnyMisappliedName()){
454
//						logger.warn(taxRel.getUuid() + "; " + taxRel.getToTaxon().getUuid() + " + " + taxRel.getToTaxon().getTitleCache());
455
        			if (isPesiTaxon(taxRel.getToTaxon(), true)){
456
        				return true;
457
        			}
458
        		}
459
        	}
460
        	if (logger.isDebugEnabled()){ logger.debug("Misapplied name has no accepted PESI taxon: " +  taxon.getUuid() + ", (" +  taxon.getTitleCache() + ")");}
461
        	return false;
462
        }
463
    }
464

    
465
	@Override
466
    protected Object getDbIdCdmWithExceptions(CdmBase cdmBase, PesiExportState state) {
467
		if (cdmBase.isInstanceOf(TaxonName.class)){
468
		    TaxonName name = CdmBase.deproxy(cdmBase, TaxonName.class);
469
		    if (name.getTaxonBases().size()>1){
470
		        logger.warn("Name has multiple taxa. Can't define correct ID. Use first one." + name.getUuid());
471
		    }
472
		    if (!name.getTaxonBases().isEmpty()){
473
		        TaxonBase<?> tb = name.getTaxonBases().iterator().next();
474
		        return this.getDbId(tb, state);
475
            }else{
476
                return ( cdmBase.getId() + state.getConfig().getNameIdStart() );
477
            }
478
		}else if (isAdditionalSource(cdmBase) ){
479
			return ( cdmBase.getId() + 2 * state.getConfig().getNameIdStart() );  //make it a separate variable if conflicts occur.
480
		}else{
481
			return super.getDbIdCdmWithExceptions(cdmBase, state);
482
		}
483
	}
484

    
485
	private boolean isAdditionalSource(CdmBase cdmBase) {
486
		if (cdmBase.isInstanceOf(TextData.class)){
487
			TextData textData = CdmBase.deproxy(cdmBase, TextData.class);
488
			if (textData.getFeature().equals(Feature.ADDITIONAL_PUBLICATION()) ||
489
					textData.getFeature().equals(Feature.CITATION())){
490
				return true;
491
			}
492
		}
493
		return false;
494
	}
495

    
496
	protected MarkerType getUuidMarkerType(UUID uuid, PesiExportState state){
497
		if (uuid == null){
498
			uuid = UUID.randomUUID();
499
		}
500

    
501
		MarkerType markerType = state.getMarkerType(uuid);
502
		if (markerType == null){
503
			if (uuid.equals(PesiTransformer.uuidMarkerGuidIsMissing)){
504
				markerType = MarkerType.NewInstance("Uuid is Missing", "Uuid is missing", null);
505
				markerType.setUuid(uuid);
506
			} else if (uuid.equals(PesiTransformer.uuidMarkerTypeHasNoLastAction)){
507
				markerType = MarkerType.NewInstance("Has no last Action", "Has no last action", null);
508
				markerType.setUuid(uuid);
509
			}
510
		}
511

    
512
		state.putMarkerType(markerType);
513
		return markerType;
514
	}
515

    
516
	protected static TaxonNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
517
	    TaxonNameDefaultCacheStrategy cacheStrategy;
518
		if (taxonName.isZoological()){
519
			cacheStrategy = zooNameStrategy;
520
		}else if (taxonName.isBotanical()) {
521
			cacheStrategy = botanicalNameStrategy;
522
		}else{
523
			logger.error("Unhandled taxon name type. Can't define strategy class");
524
			cacheStrategy = botanicalNameStrategy;
525
		}
526
		return cacheStrategy;
527
	}
528

    
529
	/**
530
	 * Checks whether a given taxon is a misapplied name.
531
	 * @param taxon The {@link TaxonBase Taxon}.
532
	 * @return Whether the given TaxonName is a misapplied name or not.
533
	 */
534
	protected static boolean isMisappliedName(TaxonBase<?> taxon) {
535
		return getAcceptedTaxonForMisappliedName(taxon) != null;
536
	}
537

    
538
	/**
539
     * Checks whether a given taxon is a pro parte or partial synonym.
540
     * @param taxon The {@link TaxonBase Taxon}.
541
     * @return <code>true</code> if the the given taxon is a pp or partial synonym
542
     */
543
    protected static boolean isProParteOrPartialSynonym(TaxonBase<?> taxon) {
544
        return getAcceptedTaxonForProParteSynonym(taxon) != null;
545
    }
546

    
547
	/**
548
	 * Returns the first accepted taxon for this misapplied name.
549
	 * If this misapplied name is not a misapplied name, <code>null</code> is returned.
550
	 * @param taxon The {@link TaxonBase Taxon}.
551
	 */
552
	protected static Taxon getAcceptedTaxonForMisappliedName(TaxonBase<?> taxon) {
553
		if (! taxon.isInstanceOf(Taxon.class)){
554
			return null;
555
		}
556
		Set<TaxonRelationship> taxonRelations = CdmBase.deproxy(taxon, Taxon.class).getRelationsFromThisTaxon();
557
		for (TaxonRelationship taxonRelationship : taxonRelations) {
558
			TaxonRelationshipType taxonRelationshipType = taxonRelationship.getType();
559
			if (taxonRelationshipType.isAnyMisappliedName()) {
560
				return taxonRelationship.getToTaxon();
561
			}
562
		}
563
		return null;
564
	}
565

    
566
    protected static Taxon getAcceptedTaxonForProParteSynonym(TaxonBase<?> taxon) {
567
        if (! taxon.isInstanceOf(Taxon.class)){
568
            return null;
569
        }
570
        Set<TaxonRelationship> taxonRelations = CdmBase.deproxy(taxon, Taxon.class).getRelationsFromThisTaxon();
571
        for (TaxonRelationship taxonRelationship : taxonRelations) {
572
            TaxonRelationshipType taxonRelationshipType = taxonRelationship.getType();
573
            if (taxonRelationshipType.isAnySynonym()) {
574
                return taxonRelationship.getToTaxon();
575
            }
576
        }
577
        return null;
578
    }
579

    
580
    protected List<AnnotationType> getLastActionAnnotationTypes() {
581
        Set<UUID> uuidSet = new HashSet<>();
582
        uuidSet.add(DbLastActionMapper.uuidAnnotationTypeLastActionDate);
583
        uuidSet.add(DbLastActionMapper.uuidAnnotationTypeLastAction);
584
        uuidSet.add(ErmsTransformer.uuidAnnSpeciesExpertName);
585
        @SuppressWarnings({"unchecked","rawtypes"})
586
        List<AnnotationType> result = (List)getTermService().find(uuidSet);
587
        return result;
588
    }
589

    
590
    protected enum PesiSource{
591
        EM,
592
        FE,
593
        ERMS,
594
        IF;
595

    
596
        private PesiSource(){
597

    
598
        }
599
    }
600

    
601
    /**
602
     * Returns the source (E+M, Fauna Europaea, Index Fungorum, ERMS) of a given
603
     * Identifiable Entity as an {@link EnumSet enum set}
604
     */
605
    protected static EnumSet<PesiSource> getSources(IdentifiableEntity<?> entity){
606
        EnumSet<PesiSource> result = EnumSet.noneOf(PesiSource.class);
607

    
608
        Set<IdentifiableSource> sources = getPesiSources(entity);
609
        for (IdentifiableSource source : sources) {
610
            Reference ref = source.getCitation();
611
            UUID refUuid = ref.getUuid();
612
            if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed)){
613
                result.add(PesiSource.EM);
614
            }else if (refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)){
615
                result.add(PesiSource.FE);
616
            }else if (refUuid.equals(PesiTransformer.uuidSourceRefErms)){
617
                result.add(PesiSource.ERMS);
618
            }else if (refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum)){
619
                result.add(PesiSource.IF);
620
            }else{
621
                if (logger.isDebugEnabled()){logger.debug("Not a PESI source");}
622
            }
623
        }
624
        return result;
625
    }
626

    
627
    /**
628
     * Returns the Sources for a given TaxonName only.
629
     * @param identifiableEntity
630
     * @return The Sources.
631
     */
632
    protected static Set<IdentifiableSource> getPesiSources(IdentifiableEntity<?> identifiableEntity) {
633
        Set<IdentifiableSource> sources = new HashSet<>();
634

    
635
        //Taxon Names
636
        if (identifiableEntity.isInstanceOf(TaxonName.class)){
637
            // Sources from TaxonName
638
            TaxonName taxonName = CdmBase.deproxy(identifiableEntity, TaxonName.class);
639
            Set<IdentifiableSource> testSources = identifiableEntity.getSources();
640
            sources = filterPesiSources(testSources);
641

    
642
            if (sources.size() == 0 && testSources.size()>0){
643
                IdentifiableSource source = testSources.iterator().next();
644
                logger.warn("There are sources, but they are no pesi sources!!!" + source.getIdInSource() + " - " + source.getIdNamespace() + " - " + (source.getCitation()== null? "no reference" : source.getCitation().getTitleCache()));
645
            }
646
            if (sources.size() > 1) {
647
                logger.warn("This TaxonName has more than one Source: " + identifiableEntity.getUuid() + " (" + identifiableEntity.getTitleCache() + ")");
648
            }
649

    
650
            // name has no PESI source, take sources from TaxonBase
651
            if (sources == null || sources.isEmpty()) {
652
                Set<TaxonBase> taxa = taxonName.getTaxonBases();
653
                for (TaxonBase<?> taxonBase: taxa){
654
                    sources.addAll(filterPesiSources(taxonBase.getSources()));
655
                }
656
            }
657

    
658
        //for TaxonBases
659
        }else if (identifiableEntity.isInstanceOf(TaxonBase.class)){
660
            sources = filterPesiSources(identifiableEntity.getSources());
661
        } else {
662
            sources = filterPesiSources(identifiableEntity.getSources());
663
        }
664

    
665
        return sources;
666
    }
667

    
668
    // return all sources with a PESI reference
669
    protected static Set<IdentifiableSource> filterPesiSources(Set<? extends IdentifiableSource> sources) {
670
        Set<IdentifiableSource> result = new HashSet<>();
671
        for (IdentifiableSource source : sources){
672
            Reference ref = source.getCitation();
673
            if (ref != null){
674
                UUID refUuid = ref.getUuid();
675
                if (refUuid.equals(PesiTransformer.uuidSourceRefEuroMed) ||
676
                        refUuid.equals(PesiTransformer.uuidSourceRefFaunaEuropaea)||
677
                        refUuid.equals(PesiTransformer.uuidSourceRefErms)||
678
                        refUuid.equals(PesiTransformer.uuidSourceRefIndexFungorum) ||
679
                        refUuid.equals(PesiTransformer.uuidSourceRefAuct)){
680
                    result.add(source);
681
                }
682
            }
683
        }
684
        return result;
685
    }
686

    
687
}
(6-6/14)