Project

General

Profile

Download (18.4 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.HashSet;
13
import java.util.Iterator;
14
import java.util.List;
15
import java.util.Set;
16
import java.util.UUID;
17

    
18
import org.apache.log4j.Logger;
19

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

    
47
/**
48
 * @author e.-m.lee
49
 * @since 12.02.2010
50
 */
51
public abstract class PesiExportBase
52
              extends DbExportBase<PesiExportConfigurator, PesiExportState, PesiTransformer> {
53

    
54
    private static final long serialVersionUID = 6226747017958138156L;
55
    private static final Logger logger = Logger.getLogger(PesiExportBase.class);
56

    
57
	protected static final boolean IS_CACHE = true;
58

    
59
	private static Set<NameRelationshipType> excludedRelTypes = new HashSet<>();
60

    
61
	private static TaxonNameDefaultCacheStrategy zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
62
	private static TaxonNameDefaultCacheStrategy botanicalNameStrategy = TaxonNameDefaultCacheStrategy.NewInstance();
63

    
64
	public PesiExportBase() {
65
		super();
66
	}
67

    
68
	protected <CLASS extends TaxonBase> List<CLASS> getNextTaxonPartition(Class<CLASS> clazz, int limit,
69
	        int partitionCount, List<String> propertyPath) {
70

    
71
	    List<OrderHint> orderHints = new ArrayList<>();
72
		orderHints.add(new OrderHint("id", OrderHint.SortOrder.ASCENDING ));
73

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

    
76
		if (list.isEmpty()){
77
			return null;
78
		}
79

    
80
		Iterator<CLASS> it = list.iterator();
81
		while (it.hasNext()){
82
			TaxonBase<?> taxonBase = it.next();
83
			if (! isPesiTaxon(taxonBase)){
84
				it.remove();
85
			}
86
			taxonBase = null;
87
		}
88
		it = null;
89
		return list;
90
	}
91

    
92
	protected List<TaxonNameDescription> getNextNameDescriptionPartition(int limit, int partitionCount, List<String> propertyPath) {
93
		List<OrderHint> orderHints = new ArrayList<>();
94
		orderHints.add(new OrderHint("id", OrderHint.SortOrder.ASCENDING ));
95
		Pager<TaxonNameDescription> l = getDescriptionService().getTaxonNameDescriptions(null, limit, partitionCount, propertyPath);
96
		List<TaxonNameDescription> list = l.getRecords();
97
		if (list.isEmpty()){
98
			return null;
99
		}
100

    
101
		Iterator<TaxonNameDescription> it = list.iterator();
102
		while (it.hasNext()){
103
			TaxonNameDescription nameDescription = it.next();
104
			if (! isPesiNameDescriptionTaxon(nameDescription)){
105
				it.remove();
106
			}
107
		}
108
		return list;
109
	}
110

    
111
	private boolean isPesiNameDescriptionTaxon(TaxonNameDescription nameDescription) {
112
		TaxonName name = nameDescription.getTaxonName();
113
		if (isPurePesiName(name)){
114
			return true;
115
		}else{
116
			Set<TaxonBase> taxa = name.getTaxonBases();
117
			for (TaxonBase<?> taxonBase : taxa){
118
				if (isPesiTaxon(taxonBase)){
119
					return true;
120
				}
121
			}
122
		}
123
		return false;
124
	}
125

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

    
134
		List<TaxonName> list = getNameService().list(clazz, limit, partitionCount * limit, orderHints, null);
135
		if (list.isEmpty()){
136
			return null;
137
		}
138
		Iterator<TaxonName> it = list.iterator();
139
		while (it.hasNext()){
140
		    TaxonName taxonName = HibernateProxyHelper.deproxy(it.next());
141
			if (! isPurePesiName(taxonName)){
142
				it.remove();
143
			}
144
		}
145
		return list;
146
	}
147

    
148
	protected <CLASS extends RelationshipBase> List<CLASS> getNextNameRelationshipPartition(
149
	                Class<CLASS> clazz, int pageSize, int partitionCount, List<String> propertyPaths) {
150

    
151
	    List<CLASS> result = new ArrayList<>();
152
		List<OrderHint> orderHints = null;
153
		List<CLASS> list;
154
		if (NameRelationship.class.isAssignableFrom(clazz)){
155
            list = (List<CLASS>)getNameService().listNameRelationships(null, pageSize, partitionCount, orderHints, propertyPaths);
156
        }else if (HybridRelationship.class.isAssignableFrom(clazz)){
157
            list = (List<CLASS>)getNameService().listHybridRelationships(null, pageSize, partitionCount, orderHints, propertyPaths);
158
        }else{
159
            throw new RuntimeException("Only NameRelationship or HybridRelationship allowed here");
160
        }
161
		if (list.isEmpty()){
162
			return null;
163
		}
164
		for (CLASS rel : list){
165
			if (isPesiNameRelationship(rel)){
166
				result.add(rel);
167
			}
168
		}
169
		return result;
170
	}
171

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

    
174
	    List<CLASS> result = new ArrayList<>();
175
		List<OrderHint> orderHints = null;
176

    
177
		List<CLASS> list = (List<CLASS>)this.getTaxonService()
178
		        .listTaxonRelationships(null, limit, partitionCount, orderHints, propertyPaths);
179

    
180
		if (list.isEmpty()){
181
			return null;
182
		}
183

    
184
		for (CLASS rel : list){
185
			if (isPesiTaxonOrSynonymRelationship(rel)){
186
				result.add(rel);
187
			}
188
		}
189
		return result;
190
	}
191

    
192
	protected boolean isPesiNameRelationship(RelationshipBase<?,?,?> rel){
193
		TaxonName name1;
194
		TaxonName name2;
195
		if (rel.isInstanceOf(HybridRelationship.class)){
196
			HybridRelationship hybridRel = CdmBase.deproxy(rel, HybridRelationship.class);
197
			name1 = hybridRel.getParentName();
198
			name2 = hybridRel.getHybridName();
199
		}else if (rel.isInstanceOf(NameRelationship.class)){
200
			NameRelationship nameRel = CdmBase.deproxy(rel, NameRelationship.class);
201
			name1 = nameRel.getFromName();
202
			name2 = nameRel.getToName();
203
		}else{
204
			logger.warn ("Only hybrid- and name-relationships alowed here");
205
			return false;
206
		}
207
		return (isPesiName(name1) && isPesiName(name2));
208
	}
209

    
210
	private boolean isPesiName(TaxonName name) {
211
		return hasPesiTaxon(name) || isPurePesiName(name);
212
	}
213

    
214
	protected boolean isPesiTaxonOrSynonymRelationship(RelationshipBase rel){
215
		TaxonBase<?> fromTaxon;
216
		Taxon toTaxon;
217
		// TODO:fix!!!
218
//		if (rel.isInstanceOf(SynonymRelationship.class)){
219
//			SynonymRelationship synRel = CdmBase.deproxy(rel, SynonymRelationship.class);
220
//			fromTaxon = synRel.getSynonym();
221
//			toTaxon = synRel.getAcceptedTaxon();
222
//			synRel = null;
223
//		}else
224
		if (rel.isInstanceOf(TaxonRelationship.class)){
225
			TaxonRelationship taxRel = CdmBase.deproxy(rel, TaxonRelationship.class);
226
			fromTaxon = taxRel.getFromTaxon();
227
			toTaxon = taxRel.getToTaxon();
228
			taxRel = null;
229
		}else{
230
			logger.warn ("Only synonym - and taxon-relationships allowed here");
231
			return false;
232
		}
233
		return (isPesiTaxon(fromTaxon, false) && isPesiTaxon(toTaxon, true));
234
	}
235

    
236
	/**
237
	 * Decides if a name is not used as the name part of a PESI taxon (and therefore is
238
	 * exported to PESI as taxon already) but is related to a name used as a PESI taxon
239
	 * (e.g. as basionym, orthographic variant, etc.) and therefore should be exported
240
	 * to PESI as part of the name relationship.
241
	 * @param taxonName
242
	 * @return
243
	 */
244
	protected boolean isPurePesiName(TaxonName taxonName){
245
		if (hasPesiTaxon(taxonName)){
246
			return false;
247
		}
248

    
249
		//from names
250
		for (NameRelationship rel :taxonName.getRelationsFromThisName()){
251
			TaxonName relatedName = rel.getToName();
252
			if (hasPesiTaxon(relatedName)){
253
				return true;
254
			}
255
		}
256

    
257
		//excluded relationships on to-side
258
		initExcludedRelTypes();
259

    
260
		//to names
261
		for (NameRelationship rel :taxonName.getRelationsToThisName()){
262
			//exclude certain types
263
			if (excludedRelTypes.contains(rel.getType())){
264
				continue;
265
			}
266
			TaxonName relatedName = rel.getFromName();
267
			if (hasPesiTaxon(relatedName)){
268
				return true;
269
			}
270
		}
271

    
272
		//include hybrid parents, but no childs
273

    
274
		for (HybridRelationship rel : taxonName.getHybridParentRelations()){
275
			INonViralName child = rel.getHybridName();
276
			if (hasPesiTaxon(child)){
277
				return true;
278
			}
279
		}
280

    
281
		return false;
282
	}
283

    
284

    
285
	private void initExcludedRelTypes() {
286
		if (excludedRelTypes.isEmpty()){
287
			excludedRelTypes.add(NameRelationshipType.BASIONYM());
288
			excludedRelTypes.add(NameRelationshipType.REPLACED_SYNONYM());
289
			excludedRelTypes.add(NameRelationshipType.ORTHOGRAPHIC_VARIANT());
290
		}
291
	}
292

    
293

    
294
	/**
295
	 * Decides if a given name has "PESI taxa" attached.
296
	 *
297
	 * @see #getPesiTaxa(TaxonNameBase)
298
	 * @see #isPesiTaxon(TaxonBase)
299
	 * @param taxonName
300
	 * @return
301
	 */
302
	protected boolean hasPesiTaxon(INonViralName taxonName) {
303
		for (TaxonBase<?> taxon : taxonName.getTaxonBases()){
304
			if (isPesiTaxon(taxon)){
305
				return true;
306
			}
307
		}
308
		return false;
309
	}
310

    
311
	/**
312
	 * Returns those concepts (taxon bases) for the given name that
313
	 * are pesi taxa.
314
	 *
315
	 *  @see #isPesiTaxon(TaxonBase)
316
	 * @param name
317
	 * @return
318
	 */
319
	protected Set<TaxonBase<?>> getPesiTaxa(TaxonName name){
320
		Set<TaxonBase<?>> result = new HashSet<>();
321
		for (TaxonBase<?> taxonBase : name.getTaxonBases()){
322
			if (isPesiTaxon(taxonBase)){
323
				result.add(taxonBase);
324
			}
325
		}
326
		return result;
327
	}
328

    
329
	/**
330
	 * @see #isPesiTaxon(TaxonBase, boolean)
331
	 * @return
332
	 */
333
	protected static boolean isPesiTaxon(TaxonBase taxonBase) {
334
		return isPesiTaxon(taxonBase, false);
335
	}
336

    
337
	/**
338
	 * Checks if this taxon base is a taxon that is to be exported to PESI. This is generally the case
339
	 * but not for taxa that are marked as "unpublish". Synonyms and misapplied names are exported if they are
340
	 * related at least to one accepted taxon that is also exported, except for those misapplied names
341
	 * marked as misapplied names created by Euro+Med common names ({@linkplain http://dev.e-taxonomy.eu/trac/ticket/2786} ).
342
	 * The list of conditions may change in future.
343
	 * @param taxonBase
344
	 * @return
345
	 */
346
	protected static boolean isPesiTaxon(TaxonBase taxonBase, boolean excludeMisappliedNames) {
347
		if (taxonBase == null){
348
		    return false;
349
		}
350
	    //handle accepted taxa
351
		if (taxonBase.isInstanceOf(Taxon.class)){
352
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
353
			return isPesiAcceptedTaxon(excludeMisappliedNames, taxon);
354
		//handle synonyms
355
		}else if (taxonBase.isInstanceOf(Synonym.class)){
356
			Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
357
			return isPesiSynonym(synonym);
358
		}else {
359
			throw new RuntimeException("Unknown taxon base type: " + taxonBase.getClass());
360
		}
361
	}
362

    
363
    private static boolean isPesiSynonym(Synonym synonym) {
364
        boolean hasAcceptedPesiTaxon = false;
365
        hasAcceptedPesiTaxon = isPesiTaxon(synonym.getAcceptedTaxon());
366

    
367
        if (!hasAcceptedPesiTaxon) {if (logger.isDebugEnabled()){logger.debug("Synonym has no accepted PESI taxon: " +  synonym.getUuid() + ", (" +  synonym.getTitleCache() + ")");}}
368
        synonym = null;
369
        return hasAcceptedPesiTaxon;
370
    }
371

    
372
    private static boolean isPesiAcceptedTaxon(boolean excludeMisappliedNames, Taxon taxon) {
373
        if (! taxon.isPublish()){
374
        	taxon = null;
375
        	return false;
376
        }
377
        for (Marker marker : taxon.getMarkers()){
378
        	//probably not needed anymore after #1780 was fixed, also #4046 interesting
379
        	if (marker.getValue() == false && marker.getMarkerType().equals(MarkerType.PUBLISH())){
380
        		taxon = null;
381
        		return false;
382
        	//probably not needed any more after #2786 was fixed
383
        	}else if (marker.getValue() == true && marker.getMarkerType().getUuid().equals(BerlinModelTransformer.uuidMisappliedCommonName)){
384
        		logger.warn("Misapplied common name still exists");
385
        		taxon = null;
386
        		return false;
387
        	}
388
        }
389

    
390
        //handle PESI accepted taxa
391
        if (! taxon.isMisapplication()){
392
        	for (Marker marker : taxon.getMarkers()){
393
        		if (marker.getValue() == false && marker.getMarkerType().equals(MarkerType.PUBLISH())){
394
        			taxon = null;
395
        			return false;
396
        		}
397
        	}
398
        	return true;
399
        //handle misapplied names
400
        }else{
401
        	if (excludeMisappliedNames){
402
        		taxon = null;
403
        		return false;
404
        	}
405
        	for (Marker marker : taxon.getMarkers()){
406
        		//probably not needed any more after #2786 was fixed
407
        		if (marker.getValue() == true && marker.getMarkerType().getUuid().equals(BerlinModelTransformer.uuidMisappliedCommonName)){
408
        			logger.warn("Misapplied common name still exists");
409
        			taxon = null;
410
        			return false;
411
        		}
412
        	}
413
        	for (TaxonRelationship taxRel : taxon.getRelationsFromThisTaxon()){
414
        		if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
415
//						logger.warn(taxRel.getUuid() + "; " + taxRel.getToTaxon().getUuid() + " + " + taxRel.getToTaxon().getTitleCache());
416
        			if (isPesiTaxon(taxRel.getToTaxon(), true)){
417
        				taxon = null;
418
        				return true;
419
        			}
420
        		}
421
        	}
422
        	if (logger.isDebugEnabled()){ logger.debug("Misapplied name has no accepted PESI taxon: " +  taxon.getUuid() + ", (" +  taxon.getTitleCache() + ")");}
423
        	taxon = null;
424
        	return false;
425
        }
426
    }
427

    
428
	@Override
429
    protected Object getDbIdCdmWithExceptions(CdmBase cdmBase, PesiExportState state) {
430
		if (cdmBase.isInstanceOf(TaxonName.class)){
431
		    TaxonName name = CdmBase.deproxy(cdmBase, TaxonName.class);
432
		    if (name.getTaxonBases().size()>1){
433
		        logger.warn("Name has multiple taxa. Can't define correct ID. Use first one." + name.getUuid());
434
		    }
435
		    if (!name.getTaxonBases().isEmpty()){
436
		        TaxonBase<?> tb = name.getTaxonBases().iterator().next();
437
		        return this.getDbId(tb, state);
438
            }else{
439
                return ( cdmBase.getId() + state.getConfig().getNameIdStart() );
440
            }
441
		}else if (isAdditionalSource(cdmBase) ){
442
			return ( cdmBase.getId() + 2 * state.getConfig().getNameIdStart() );  //make it a separate variable if conflicts occur.
443
		}else{
444
			return super.getDbIdCdmWithExceptions(cdmBase, state);
445
		}
446
	}
447

    
448
	private boolean isAdditionalSource(CdmBase cdmBase) {
449
		if (cdmBase.isInstanceOf(TextData.class)){
450
			TextData textData = CdmBase.deproxy(cdmBase, TextData.class);
451
			if (textData.getFeature().equals(Feature.ADDITIONAL_PUBLICATION()) ||
452
					textData.getFeature().equals(Feature.CITATION())){
453
				return true;
454
			}
455
		}
456
		return false;
457
	}
458

    
459
	protected MarkerType getUuidMarkerType(UUID uuid, PesiExportState state){
460
		if (uuid == null){
461
			uuid = UUID.randomUUID();
462
		}
463

    
464
		MarkerType markerType = state.getMarkerType(uuid);
465
		if (markerType == null){
466
			if (uuid.equals(PesiTransformer.uuidMarkerGuidIsMissing)){
467
				markerType = MarkerType.NewInstance("Uuid is Missing", "Uuid is missing", null);
468
				markerType.setUuid(uuid);
469
			} else if (uuid.equals(PesiTransformer.uuidMarkerTypeHasNoLastAction)){
470
				markerType = MarkerType.NewInstance("Has no last Action", "Has no last action", null);
471
				markerType.setUuid(uuid);
472
			}
473
		}
474

    
475
		state.putMarkerType(markerType);
476
		return markerType;
477
	}
478

    
479
	protected static TaxonNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
480
	    TaxonNameDefaultCacheStrategy cacheStrategy;
481
		if (taxonName.isZoological()){
482
			cacheStrategy = zooNameStrategy;
483
		}else if (taxonName.isBotanical()) {
484
			cacheStrategy = botanicalNameStrategy;
485
		}else{
486
			logger.error("Unhandled taxon name type. Can't define strategy class");
487
			cacheStrategy = botanicalNameStrategy;
488
		}
489
		return cacheStrategy;
490
	}
491

    
492
	/**
493
	 * Checks whether a given taxon is a misapplied name.
494
	 * @param taxon The {@link TaxonBase Taxon}.
495
	 * @return Whether the given TaxonName is a misapplied name or not.
496
	 */
497
	protected static boolean isMisappliedName(TaxonBase<?> taxon) {
498
		return getAcceptedTaxonForMisappliedName(taxon) != null;
499
	}
500

    
501
	/**
502
	 * Returns the first accepted taxon for this misapplied name.
503
	 * If this misapplied name is not a misapplied name, <code>null</code> is returned.
504
	 * @param taxon The {@link TaxonBase Taxon}.
505
	 */
506
	private static Taxon getAcceptedTaxonForMisappliedName(TaxonBase<?> taxon) {
507
		if (! taxon.isInstanceOf(Taxon.class)){
508
			return null;
509
		}
510
		Set<TaxonRelationship> taxonRelations = CdmBase.deproxy(taxon, Taxon.class).getRelationsFromThisTaxon();
511
		for (TaxonRelationship taxonRelationship : taxonRelations) {
512
			TaxonRelationshipType taxonRelationshipType = taxonRelationship.getType();
513
			if (taxonRelationshipType.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())) {
514
				return taxonRelationship.getToTaxon();
515
			}
516
		}
517
		return null;
518
	}
519

    
520
    protected List<AnnotationType> getLastActionAnnotationTypes() {
521
        Set<UUID> uuidSet = new HashSet<>();
522
        uuidSet.add(DbLastActionMapper.uuidAnnotationTypeLastActionDate);
523
        uuidSet.add(DbLastActionMapper.uuidAnnotationTypeLastAction);
524
        @SuppressWarnings({ "unchecked", "rawtypes" })
525
        List<AnnotationType> result = (List)getTermService().find(uuidSet);
526
        return result;
527
    }
528

    
529
}
(5-5/13)