Project

General

Profile

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

    
111
	private boolean isPesiNameDescriptionTaxon(TaxonNameDescription nameDescription) {
112
		TaxonNameBase<?,?> 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
		}
124
		return false;
125
	}
126

    
127

    
128
	/**
129
	 * Returns the next list of pure names. If finished result will be null. If list is empty there may be result in further partitions.
130
	 * @param clazz
131
	 * @param limit
132
	 * @param partitionCount
133
	 * @return
134
	 */
135
	protected List<NonViralName<?>> getNextPureNamePartition(Class<? extends NonViralName> clazz,int limit, int partitionCount) {
136
		List<OrderHint> orderHints = new ArrayList<OrderHint>();
137
		orderHints.add(new OrderHint("id", OrderHint.SortOrder.ASCENDING ));
138
		List<String> propPath = Arrays.asList(new String[]{"taxonBases"});
139
		
140
		List<NonViralName<?>> list = (List)getNameService().list(clazz, limit, partitionCount * limit, orderHints, null);
141
		if (list.isEmpty()){
142
			return null;
143
		}
144
		Iterator<NonViralName<?>> it = list.iterator();
145
		while (it.hasNext()){
146
			NonViralName<?> taxonName = HibernateProxyHelper.deproxy(it.next(), NonViralName.class);
147
			if (! isPurePesiName(taxonName)){
148
				it.remove();
149
			}
150
		}
151
		return list;
152
	}
153
	
154
	protected <CLASS extends RelationshipBase> List<CLASS> getNextNameRelationshipPartition(Class<CLASS> clazz, int limit, int partitionCount, List<String> propertyPath) {
155
		List<CLASS> result = new ArrayList<CLASS>();
156
		String[] propertyPaths = null;
157
		String orderHints = null;
158
		List<CLASS> list = (List<CLASS>)getNameService().getAllRelationships(limit, partitionCount * limit);
159
		if (list.isEmpty()){
160
			return null;
161
		}
162
		for (CLASS rel : list){
163
			if (isPesiNameRelationship(rel)){
164
				result.add(rel);
165
			}
166
		}
167
		return result;
168
	}
169
	
170
	protected <CLASS extends RelationshipBase> List<CLASS> getNextTaxonRelationshipPartition( int limit, int partitionCount, List<String> propertyPath) {
171
		List<CLASS> result = new ArrayList<CLASS>();
172
		String[] propertyPaths = null;
173
		String orderHints = null;
174
		List<CLASS> list = (List<CLASS>)getTaxonService().getAllRelationships(limit, partitionCount * limit);
175
		
176
		if (list.isEmpty()){
177
			return null;
178
		}
179
		
180
		for (CLASS rel : list){
181
			if (isPesiTaxonOrSynonymRelationship(rel)){
182
				result.add(rel);
183
			}
184
		}
185
		return result;
186
	}
187
	
188
	protected boolean isPesiNameRelationship(RelationshipBase rel){
189
		TaxonNameBase<?,?> name1;
190
		TaxonNameBase<?,?> name2;
191
		if (rel.isInstanceOf(HybridRelationship.class)){
192
			HybridRelationship hybridRel = CdmBase.deproxy(rel, HybridRelationship.class);
193
			name1 = hybridRel.getParentName();
194
			name2 = hybridRel.getHybridName();
195
		}else if (rel.isInstanceOf(NameRelationship.class)){
196
			NameRelationship nameRel = CdmBase.deproxy(rel, NameRelationship.class);
197
			name1 = nameRel.getFromName();
198
			name2 = nameRel.getToName();
199
		}else{
200
			logger.warn ("Only hybrid- and name-relationships alowed here");
201
			return false;
202
		}
203
		return (isPesiName(name1) && isPesiName(name2));
204
		
205
	}
206
	
207
	private boolean isPesiName(TaxonNameBase<?,?> name) {
208
		return hasPesiTaxon(name) || isPurePesiName(name);
209
	}
210

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

    
232
	
233

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

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

    
291

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

    
327

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

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

    
442

    
443
	protected MarkerType getUuidMarkerType(UUID uuid, PesiExportState state){
444
		if (uuid == null){
445
			uuid = UUID.randomUUID();
446
		}
447
		
448
		MarkerType markerType = state.getMarkerType(uuid);
449
			if (markerType == null){
450
				if (uuid.equals(PesiTransformer.uuidMarkerGuidIsMissing)){
451
					markerType = MarkerType.NewInstance("Uuid is Missing", "Uuid is missing", null);
452
					markerType.setUuid(uuid);
453
				} else if (uuid.equals(PesiTransformer.uuidMarkerTypeHasNoLastAction)){
454
					markerType = MarkerType.NewInstance("Has no last Action", "Has no last action", null);
455
					markerType.setUuid(uuid);
456
				}
457
			}
458

    
459
			state.putMarkerType(markerType);
460
			return markerType;
461
		}
462
	
463
	
464
	
465
	protected static NonViralNameDefaultCacheStrategy getCacheStrategy(TaxonNameBase<?, ?> taxonName) {
466
		NonViralNameDefaultCacheStrategy cacheStrategy;
467
		if (taxonName.isInstanceOf(ZoologicalName.class)){
468
			cacheStrategy = zooNameStrategy;
469
		}else if (taxonName.isInstanceOf(BotanicalName.class)) {
470
			cacheStrategy = botanicalNameStrategy;
471
		}else{
472
			logger.error("Unhandled taxon name type. Can't define strategy class");
473
			cacheStrategy = botanicalNameStrategy;
474
		}
475
		return cacheStrategy;
476
	}
477
	
478

    
479
	
480
	
481
	/**
482
	 * Checks whether a given taxon is a misapplied name.
483
	 * @param taxon The {@link TaxonBase Taxon}.
484
	 * @return Whether the given TaxonName is a misapplied name or not.
485
	 */
486
	protected static boolean isMisappliedName(TaxonBase<?> taxon) {
487
		return getAcceptedTaxonForMisappliedName(taxon) != null;
488
		
489
	}
490
	
491

    
492
	/**
493
	 * Returns the first accepted taxon for this misapplied name.
494
	 * If this misapplied name is not a misapplied name, <code>null</code> is returned. 
495
	 * @param taxon The {@link TaxonBase Taxon}.
496
	 */
497
	private static Taxon getAcceptedTaxonForMisappliedName(TaxonBase<?> taxon) {
498
		if (! taxon.isInstanceOf(Taxon.class)){
499
			return null;
500
		}
501
		Set<TaxonRelationship> taxonRelations = CdmBase.deproxy(taxon, Taxon.class).getRelationsFromThisTaxon();
502
		for (TaxonRelationship taxonRelationship : taxonRelations) {
503
			TaxonRelationshipType taxonRelationshipType = taxonRelationship.getType();
504
			if (taxonRelationshipType.equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())) {
505
				return taxonRelationship.getToTaxon();
506
			}
507
		}
508
		return null;
509
	}
510

    
511

    
512

    
513

    
514

    
515
	
516
	
517
	
518

    
519
//	protected List<TaxonBase> getNextDescriptionPartition(Class<? extends DescriptionElementBase> clazz,int limit, int partitionCount) {
520
//		List<DescriptionElementBase> list = getDescriptionService().listDescriptionElements(null, null, pageSize, pageNumber, propPath);
521
//		
522
//		Iterator<TaxonBase> it = list.iterator();
523
//		while (it.hasNext()){
524
//			TaxonBase<?> taxonBase = it.next();
525
//			if (! isPesiTaxon(taxonBase)){
526
//				it.remove();
527
//			}
528
//		}
529
//		return list;
530
//	}
531

    
532
}
(4-4/12)