Project

General

Profile

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

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

    
20
import org.apache.log4j.Logger;
21

    
22
import eu.etaxonomy.cdm.api.service.pager.Pager;
23
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
24
import eu.etaxonomy.cdm.io.berlinModel.BerlinModelTransformer;
25
import eu.etaxonomy.cdm.io.common.DbExportBase;
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.BotanicalName;
34
import eu.etaxonomy.cdm.model.name.HybridRelationship;
35
import eu.etaxonomy.cdm.model.name.NameRelationship;
36
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
37
import eu.etaxonomy.cdm.model.name.NonViralName;
38
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
39
import eu.etaxonomy.cdm.model.name.ZoologicalName;
40
import eu.etaxonomy.cdm.model.taxon.Synonym;
41
import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
42
import eu.etaxonomy.cdm.model.taxon.Taxon;
43
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
44
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
45
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
46
import eu.etaxonomy.cdm.persistence.query.OrderHint;
47
import eu.etaxonomy.cdm.strategy.cache.name.BotanicNameDefaultCacheStrategy;
48
import eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy;
49
import eu.etaxonomy.cdm.strategy.cache.name.ZooNameNoMarkerCacheStrategy;
50

    
51
/**
52
 * @author e.-m.lee
53
 * @date 12.02.2010
54
 *
55
 */
56
public abstract class PesiExportBase extends DbExportBase<PesiExportConfigurator, PesiExportState, PesiTransformer> {
57
	private static final Logger logger = Logger.getLogger(PesiExportBase.class);
58
	
59
	protected static final boolean IS_CACHE = true;
60
	
61
	private static Set<NameRelationshipType> excludedRelTypes = new HashSet<NameRelationshipType>();
62
	
63
	private static NonViralNameDefaultCacheStrategy<?> zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
64
	private static NonViralNameDefaultCacheStrategy<?> botanicalNameStrategy = BotanicNameDefaultCacheStrategy.NewInstance();
65
	
66
	public PesiExportBase() {
67
		super();
68
	}
69
	
70

    
71
	protected <CLASS extends TaxonBase> List<CLASS> getNextTaxonPartition(Class<CLASS> clazz, int limit, int partitionCount, List<String> propertyPath) {
72
		List<OrderHint> orderHints = new ArrayList<OrderHint>();
73
		orderHints.add(new OrderHint("id", OrderHint.SortOrder.ASCENDING ));
74
		
75
		List<CLASS> list = (List<CLASS>)getTaxonService().list(clazz, limit, partitionCount * limit, orderHints, propertyPath);
76
		
77
		
78
		
79
		if (list.isEmpty()){
80
			return null;
81
		}
82
		
83
		Iterator<CLASS> it = list.iterator();
84
		while (it.hasNext()){
85
			TaxonBase<?> taxonBase = it.next();
86
			if (! isPesiTaxon(taxonBase)){
87
				it.remove();
88
			}
89
			taxonBase = null;
90
		}
91
		it = null;
92
		return list;
93
	}
94
	
95
	protected List<TaxonNameDescription> getNextNameDescriptionPartition(int limit, int partitionCount, List<String> propertyPath) {
96
		List<OrderHint> orderHints = new ArrayList<OrderHint>();
97
		orderHints.add(new OrderHint("id", OrderHint.SortOrder.ASCENDING ));
98
		Pager<TaxonNameDescription> l = getDescriptionService().getTaxonNameDescriptions(null, limit, partitionCount, propertyPath);
99
		List<TaxonNameDescription> list = l.getRecords();
100
		if (list.isEmpty()){
101
			return null;
102
		}
103
		
104
		Iterator<TaxonNameDescription> it = list.iterator();
105
		while (it.hasNext()){
106
			TaxonNameDescription nameDescription = it.next();
107
			if (! isPesiNameDescriptionTaxon(nameDescription)){
108
				it.remove();
109
			}
110
		}
111
		return list;
112
	}
113
	
114

    
115
	private boolean isPesiNameDescriptionTaxon(TaxonNameDescription nameDescription) {
116
		TaxonNameBase<?,?> name = nameDescription.getTaxonName();
117
		if (isPurePesiName(name)){
118
			return true;
119
		}else{
120
			Set<TaxonBase> taxa = name.getTaxonBases();
121
			for (TaxonBase<?> taxonBase : taxa){
122
				if (isPesiTaxon(taxonBase)){
123
					return true;
124
				}
125
			}
126
			
127
		}
128
		return false;
129
	}
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
	 * @param clazz
135
	 * @param limit
136
	 * @param partitionCount
137
	 * @return
138
	 */
139
	protected List<NonViralName<?>> getNextPureNamePartition(Class<? extends NonViralName> clazz,int limit, int partitionCount) {
140
		List<OrderHint> orderHints = new ArrayList<OrderHint>();
141
		orderHints.add(new OrderHint("id", OrderHint.SortOrder.ASCENDING ));
142
		List<String> propPath = Arrays.asList(new String[]{"taxonBases"});
143
		
144
		List<NonViralName<?>> list = (List)getNameService().list(clazz, limit, partitionCount * limit, orderHints, null);
145
		if (list.isEmpty()){
146
			return null;
147
		}
148
		Iterator<NonViralName<?>> it = list.iterator();
149
		while (it.hasNext()){
150
			NonViralName<?> taxonName = HibernateProxyHelper.deproxy(it.next(), NonViralName.class);
151
			if (! isPurePesiName(taxonName)){
152
				it.remove();
153
			}
154
		}
155
		return list;
156
	}
157
	
158
	protected <CLASS extends RelationshipBase> List<CLASS> getNextNameRelationshipPartition(Class<CLASS> clazz, int limit, int partitionCount, List<String> propertyPath) {
159
		List<CLASS> result = new ArrayList<CLASS>();
160
		String[] propertyPaths = null;
161
		String orderHints = null;
162
		List<CLASS> list = (List<CLASS>)getNameService().getAllRelationships(limit, partitionCount * limit);
163
		if (list.isEmpty()){
164
			return null;
165
		}
166
		for (CLASS rel : list){
167
			if (isPesiNameRelationship(rel)){
168
				result.add(rel);
169
			}
170
		}
171
		return result;
172
	}
173
	
174
	protected <CLASS extends RelationshipBase> List<CLASS> getNextTaxonRelationshipPartition( int limit, int partitionCount, List<String> propertyPath) {
175
		List<CLASS> result = new ArrayList<CLASS>();
176
		String[] propertyPaths = null;
177
		String orderHints = null;
178
		List<CLASS> list = (List<CLASS>)getTaxonService().getAllRelationships(limit, partitionCount * limit);
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
		TaxonNameBase<?,?> name1;
194
		TaxonNameBase<?,?> 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
	
211
	private boolean isPesiName(TaxonNameBase<?,?> name) {
212
		return hasPesiTaxon(name) || isPurePesiName(name);
213
	}
214

    
215
	protected boolean isPesiTaxonOrSynonymRelationship(RelationshipBase rel){
216
		TaxonBase<?> fromTaxon;
217
		Taxon toTaxon;
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 if (rel.isInstanceOf(TaxonRelationship.class)){
224
			TaxonRelationship taxRel = CdmBase.deproxy(rel, TaxonRelationship.class);
225
			fromTaxon = taxRel.getFromTaxon();
226
			toTaxon = taxRel.getToTaxon();
227
			taxRel = null;
228
		}else{
229
			logger.warn ("Only synonym - and taxon-relationships allowed here");
230
			return false;
231
		}
232
		return (isPesiTaxon(fromTaxon, false) && isPesiTaxon(toTaxon, true));
233
		
234
	}
235

    
236
	
237

    
238
	/**
239
	 * Decides if a name is not used as the name part of a PESI taxon (and therefore is
240
	 * exported to PESI as taxon already) but is related to a name used as a PESI taxon
241
	 * (e.g. as basionym, orthographic variant, etc.) and therefore should be exported
242
	 * to PESI as part of the name relationship.
243
	 * @param taxonName
244
	 * @return
245
	 */
246
	protected boolean isPurePesiName(TaxonNameBase<?,?> taxonName){
247
		if (hasPesiTaxon(taxonName)){
248
			return false;
249
		}
250
		
251
		//from names
252
		for (NameRelationship rel :taxonName.getRelationsFromThisName()){
253
			TaxonNameBase<?,?> relatedName = rel.getToName();
254
			if (hasPesiTaxon(relatedName)){
255
				return true;
256
			}
257
		}
258
		
259
		//excluded relationships on to-side
260
		initExcludedRelTypes();
261
		
262
		//to names
263
		for (NameRelationship rel :taxonName.getRelationsToThisName()){
264
			//exclude certain types
265
			if (excludedRelTypes.contains(rel.getType())){
266
				continue;
267
			}
268
			TaxonNameBase<?,?> relatedName = rel.getFromName();
269
			if (hasPesiTaxon(relatedName)){
270
				return true;
271
			}
272
		}
273
		
274
		//include hybrid parents, but no childs
275
		NonViralName nvn = CdmBase.deproxy(taxonName, NonViralName.class);
276
		for (HybridRelationship rel : (Set<HybridRelationship>)nvn.getHybridParentRelations()){
277
			NonViralName<?> child = rel.getHybridName();
278
			if (hasPesiTaxon(child)){
279
				return true;
280
			}
281
		}
282
		
283
		return false;
284
	}
285
	
286

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

    
295

    
296
	/**
297
	 * Decides if a given name has "PESI taxa" attached.
298
	 * 
299
	 * @see #getPesiTaxa(TaxonNameBase)
300
	 * @see #isPesiTaxon(TaxonBase)
301
	 * @param taxonName
302
	 * @return
303
	 */
304
	protected boolean hasPesiTaxon(TaxonNameBase<?,?> taxonName) {
305
		for (TaxonBase<?> taxon : taxonName.getTaxonBases()){
306
			if (isPesiTaxon(taxon)){
307
				return true;
308
			}
309
		}
310
		return false;
311
	}
312
	
313
	/**
314
	 * Returns those concepts (taxon bases) for the given name that
315
	 * are pesi taxa.
316
	 * 
317
	 *  @see #isPesiTaxon(TaxonBase)
318
	 * @param name
319
	 * @return
320
	 */
321
	protected Set<TaxonBase<?>> getPesiTaxa(TaxonNameBase<?,?> name){
322
		Set<TaxonBase<?>> result = new HashSet<TaxonBase<?>>();
323
		for (TaxonBase<?> taxonBase : name.getTaxonBases()){
324
			if (isPesiTaxon(taxonBase)){
325
				result.add(taxonBase);
326
			}
327
		}
328
		return result;
329
	}
330

    
331

    
332
	/**
333
	 * @see #isPesiTaxon(TaxonBase, boolean)
334
	 * @param taxonBase
335
	 * @return
336
	 */
337
	protected static boolean isPesiTaxon(TaxonBase taxonBase) {
338
		return isPesiTaxon(taxonBase, false);
339
	}
340

    
341
	
342
	/**
343
	 * Checks if this taxon base is a taxon that is to be exported to PESI. This is generally the case
344
	 * but not for taxa that are marked as "unpublish". Synonyms and misapplied names are exported if they are
345
	 * related at least to one accepted taxon that is also exported, except for those misapplied names 
346
	 * marked as misapplied names created by Euro+Med common names ({@linkplain http://dev.e-taxonomy.eu/trac/ticket/2786} ).
347
	 * The list of conditions may change in future.
348
	 * @param taxonBase
349
	 * @return
350
	 */
351
	protected static boolean isPesiTaxon(TaxonBase taxonBase, boolean excludeMisappliedNames) {
352
		//handle accepted taxa
353
		if (taxonBase.isInstanceOf(Taxon.class)){
354
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
355
			if (! taxon.isPublish()){
356
				taxon = null;
357
				return false;
358
			}
359
			for (Marker marker : taxon.getMarkers()){
360
				//probably not needed anymore after #1780 was fixed, also #4046 interesting
361
				if (marker.getValue() == false && marker.getMarkerType().equals(MarkerType.PUBLISH())){
362
					taxon = null;
363
					return false;
364
				//probably not needed any more after #2786 was fixed
365
				}else if (marker.getValue() == true && marker.getMarkerType().getUuid().equals(BerlinModelTransformer.uuidMisappliedCommonName)){
366
					logger.warn("Misapplied common name still exists");
367
					taxon = null;
368
					return false;
369
				}
370
				
371
			}
372
			
373
			//handle PESI accepted taxa
374
			if (! taxon.isMisapplication()){
375
				for (Marker marker : taxon.getMarkers()){
376
					if (marker.getValue() == false && marker.getMarkerType().equals(MarkerType.PUBLISH())){
377
						taxon = null;
378
						return false;
379
					}
380
				}
381
				return true;
382
			//handle misapplied names
383
			}else{
384
				if (excludeMisappliedNames){
385
					taxon = null;
386
					return false;
387
				}
388
				for (Marker marker : taxon.getMarkers()){
389
					//probably not needed any more after #2786 was fixed
390
					if (marker.getValue() == true && marker.getMarkerType().getUuid().equals(BerlinModelTransformer.uuidMisappliedCommonName)){
391
						logger.warn("Misapplied common name still exists");
392
						taxon = null;
393
						return false;
394
					}
395
				}
396
				for (TaxonRelationship taxRel : taxon.getRelationsFromThisTaxon()){
397
					if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
398
//						logger.warn(taxRel.getUuid() + "; " + taxRel.getToTaxon().getUuid() + " + " + taxRel.getToTaxon().getTitleCache());
399
						if (isPesiTaxon(taxRel.getToTaxon(), true)){
400
							taxon = null;
401
							return true;
402
						}
403
					}
404
				}
405
				if (logger.isDebugEnabled()){ logger.debug("Misapplied name has no accepted PESI taxon: " +  taxon.getUuid() + ", (" +  taxon.getTitleCache() + ")");}
406
				taxon = null;
407
				return false;
408
			}
409
		//handle synonyms
410
		}else if (taxonBase.isInstanceOf(Synonym.class)){
411
			Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
412
			boolean hasAcceptedPesiTaxon = false;
413
			for (Taxon accTaxon : synonym.getAcceptedTaxa()){
414
				if (isPesiTaxon(accTaxon)){
415
					hasAcceptedPesiTaxon = true;
416
				}
417
			}
418
			if (!hasAcceptedPesiTaxon) {if (logger.isDebugEnabled()){logger.debug("Synonym has no accepted PESI taxon: " +  synonym.getUuid() + ", (" +  synonym.getTitleCache() + ")");}}
419
			synonym = null;
420
			return hasAcceptedPesiTaxon;
421
		}else {
422
			throw new RuntimeException("Unknown taxon base type: " + taxonBase.getClass());
423
		}
424
	}
425
	
426
	/* (non-Javadoc)
427
	 * @see eu.etaxonomy.cdm.io.common.DbExportBase#getDbIdCdmWithExceptions(eu.etaxonomy.cdm.model.common.CdmBase, eu.etaxonomy.cdm.io.common.ExportStateBase)
428
	 */
429
	protected Object getDbIdCdmWithExceptions(CdmBase cdmBase, PesiExportState state) {
430
		if (cdmBase.isInstanceOf(TaxonNameBase.class)){
431
			return ( cdmBase.getId() + state.getConfig().getNameIdStart() );
432
		}if (isAdditionalSource(cdmBase) ){
433
			return ( cdmBase.getId() + 2 * state.getConfig().getNameIdStart() );  //make it a separate variable if conflicts occur.
434
		}else{
435
			return super.getDbIdCdmWithExceptions(cdmBase, state);
436
		}
437
	}
438
	
439
	
440
	
441
	private boolean isAdditionalSource(CdmBase cdmBase) {
442
		if (cdmBase.isInstanceOf(TextData.class)){
443
			TextData textData = CdmBase.deproxy(cdmBase, TextData.class);
444
			if (textData.getFeature().equals(Feature.ADDITIONAL_PUBLICATION()) ||
445
					textData.getFeature().equals(Feature.CITATION())){
446
				return true;
447
			}
448
		}
449
		return false;
450
	}
451

    
452

    
453
	protected MarkerType getUuidMarkerType(UUID uuid, PesiExportState state){
454
		if (uuid == null){
455
			uuid = UUID.randomUUID();
456
		}
457
		
458
		MarkerType markerType = state.getMarkerType(uuid);
459
			if (markerType == null){
460
				if (uuid.equals(PesiTransformer.uuidMarkerGuidIsMissing)){
461
					markerType = MarkerType.NewInstance("Uuid is Missing", "Uuid is missing", null);
462
					markerType.setUuid(uuid);
463
				} else if (uuid.equals(PesiTransformer.uuidMarkerTypeHasNoLastAction)){
464
					markerType = MarkerType.NewInstance("Has no last Action", "Has no last action", null);
465
					markerType.setUuid(uuid);
466
				}
467
			}
468

    
469
			state.putMarkerType(markerType);
470
			return markerType;
471
		}
472
	
473
	
474
	
475
	protected static NonViralNameDefaultCacheStrategy getCacheStrategy(TaxonNameBase<?, ?> taxonName) {
476
		NonViralNameDefaultCacheStrategy cacheStrategy;
477
		if (taxonName.isInstanceOf(ZoologicalName.class)){
478
			cacheStrategy = zooNameStrategy;
479
		}else if (taxonName.isInstanceOf(BotanicalName.class)) {
480
			cacheStrategy = botanicalNameStrategy;
481
		}else{
482
			logger.error("Unhandled taxon name type. Can't define strategy class");
483
			cacheStrategy = botanicalNameStrategy;
484
		}
485
		return cacheStrategy;
486
	}
487
	
488

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

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

    
521

    
522

    
523

    
524

    
525
	
526
	
527
	
528

    
529
//	protected List<TaxonBase> getNextDescriptionPartition(Class<? extends DescriptionElementBase> clazz,int limit, int partitionCount) {
530
//		List<DescriptionElementBase> list = getDescriptionService().listDescriptionElements(null, null, pageSize, pageNumber, propPath);
531
//		
532
//		Iterator<TaxonBase> it = list.iterator();
533
//		while (it.hasNext()){
534
//			TaxonBase<?> taxonBase = it.next();
535
//			if (! isPesiTaxon(taxonBase)){
536
//				it.remove();
537
//			}
538
//		}
539
//		return list;
540
//	}
541

    
542
}
(4-4/12)