Project

General

Profile

Download (17.1 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.model.common.CdmBase;
25
import eu.etaxonomy.cdm.model.common.Marker;
26
import eu.etaxonomy.cdm.model.common.MarkerType;
27
import eu.etaxonomy.cdm.model.common.RelationshipBase;
28
import eu.etaxonomy.cdm.model.description.Feature;
29
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
30
import eu.etaxonomy.cdm.model.description.TextData;
31
import eu.etaxonomy.cdm.model.name.HybridRelationship;
32
import eu.etaxonomy.cdm.model.name.INonViralName;
33
import eu.etaxonomy.cdm.model.name.NameRelationship;
34
import eu.etaxonomy.cdm.model.name.NameRelationshipType;
35
import eu.etaxonomy.cdm.model.name.TaxonName;
36
import eu.etaxonomy.cdm.model.taxon.Synonym;
37
import eu.etaxonomy.cdm.model.taxon.Taxon;
38
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
39
import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
40
import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
41
import eu.etaxonomy.cdm.persistence.query.OrderHint;
42
import eu.etaxonomy.cdm.strategy.cache.name.TaxonNameDefaultCacheStrategy;
43
import eu.etaxonomy.cdm.strategy.cache.name.ZooNameNoMarkerCacheStrategy;
44

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

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

    
55
	protected static final boolean IS_CACHE = true;
56

    
57
	private static Set<NameRelationshipType> excludedRelTypes = new HashSet<>();
58

    
59
	private static TaxonNameDefaultCacheStrategy zooNameStrategy = ZooNameNoMarkerCacheStrategy.NewInstance();
60
	private static TaxonNameDefaultCacheStrategy botanicalNameStrategy = TaxonNameDefaultCacheStrategy.NewInstance();
61

    
62
	public PesiExportBase() {
63
		super();
64
	}
65

    
66

    
67
	protected <CLASS extends TaxonBase> List<CLASS> getNextTaxonPartition(Class<CLASS> clazz, int limit, int partitionCount, List<String> propertyPath) {
68
		List<OrderHint> orderHints = new ArrayList<OrderHint>();
69
		orderHints.add(new OrderHint("id", OrderHint.SortOrder.ASCENDING ));
70

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

    
73

    
74

    
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
		it = null;
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
		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
		}
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<TaxonName> getNextPureNamePartition(Class<TaxonName> 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<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 <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
		//TODO: fix!!!!
175

    
176
//		List<CLASS> list = (List<CLASS>)getTaxonService().getAllRelationships(limit, partitionCount * limit);
177
//
178
//		if (list.isEmpty()){
179
//			return null;
180
//		}
181
//
182
//		for (CLASS rel : list){
183
//			if (isPesiTaxonOrSynonymRelationship(rel)){
184
//				result.add(rel);
185
//			}
186
//		}
187
		return result;
188
	}
189

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

    
207
	}
208

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

    
213
	protected boolean isPesiTaxonOrSynonymRelationship(RelationshipBase rel){
214
		TaxonBase<?> fromTaxon;
215
		Taxon toTaxon;
216
		// TODO:fix!!!
217
//		if (rel.isInstanceOf(SynonymRelationship.class)){
218
//			SynonymRelationship synRel = CdmBase.deproxy(rel, SynonymRelationship.class);
219
//			fromTaxon = synRel.getSynonym();
220
//			toTaxon = synRel.getAcceptedTaxon();
221
//			synRel = null;
222
//		}else
223
		    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(TaxonName taxonName){
247
		if (hasPesiTaxon(taxonName)){
248
			return false;
249
		}
250

    
251
		//from names
252
		for (NameRelationship rel :taxonName.getRelationsFromThisName()){
253
			TaxonName 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
			TaxonName relatedName = rel.getFromName();
269
			if (hasPesiTaxon(relatedName)){
270
				return true;
271
			}
272
		}
273

    
274
		//include hybrid parents, but no childs
275

    
276
		for (HybridRelationship rel : taxonName.getHybridParentRelations()){
277
			INonViralName 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(INonViralName 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(TaxonName name){
322
		Set<TaxonBase<?>> result = new HashSet<>();
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
		if (taxonBase == null){
353
		    return false;
354
		}
355
	    //handle accepted taxa
356
		if (taxonBase.isInstanceOf(Taxon.class)){
357
			Taxon taxon = CdmBase.deproxy(taxonBase, Taxon.class);
358
			if (! taxon.isPublish()){
359
				taxon = null;
360
				return false;
361
			}
362
			for (Marker marker : taxon.getMarkers()){
363
				//probably not needed anymore after #1780 was fixed, also #4046 interesting
364
				if (marker.getValue() == false && marker.getMarkerType().equals(MarkerType.PUBLISH())){
365
					taxon = null;
366
					return false;
367
				//probably not needed any more after #2786 was fixed
368
				}else if (marker.getValue() == true && marker.getMarkerType().getUuid().equals(BerlinModelTransformer.uuidMisappliedCommonName)){
369
					logger.warn("Misapplied common name still exists");
370
					taxon = null;
371
					return false;
372
				}
373

    
374
			}
375

    
376
			//handle PESI accepted taxa
377
			if (! taxon.isMisapplication()){
378
				for (Marker marker : taxon.getMarkers()){
379
					if (marker.getValue() == false && marker.getMarkerType().equals(MarkerType.PUBLISH())){
380
						taxon = null;
381
						return false;
382
					}
383
				}
384
				return true;
385
			//handle misapplied names
386
			}else{
387
				if (excludeMisappliedNames){
388
					taxon = null;
389
					return false;
390
				}
391
				for (Marker marker : taxon.getMarkers()){
392
					//probably not needed any more after #2786 was fixed
393
					if (marker.getValue() == true && marker.getMarkerType().getUuid().equals(BerlinModelTransformer.uuidMisappliedCommonName)){
394
						logger.warn("Misapplied common name still exists");
395
						taxon = null;
396
						return false;
397
					}
398
				}
399
				for (TaxonRelationship taxRel : taxon.getRelationsFromThisTaxon()){
400
					if (taxRel.getType().equals(TaxonRelationshipType.MISAPPLIED_NAME_FOR())){
401
//						logger.warn(taxRel.getUuid() + "; " + taxRel.getToTaxon().getUuid() + " + " + taxRel.getToTaxon().getTitleCache());
402
						if (isPesiTaxon(taxRel.getToTaxon(), true)){
403
							taxon = null;
404
							return true;
405
						}
406
					}
407
				}
408
				if (logger.isDebugEnabled()){ logger.debug("Misapplied name has no accepted PESI taxon: " +  taxon.getUuid() + ", (" +  taxon.getTitleCache() + ")");}
409
				taxon = null;
410
				return false;
411
			}
412
		//handle synonyms
413
		}else if (taxonBase.isInstanceOf(Synonym.class)){
414
			Synonym synonym = CdmBase.deproxy(taxonBase, Synonym.class);
415
			boolean hasAcceptedPesiTaxon = false;
416
			hasAcceptedPesiTaxon = isPesiTaxon(synonym.getAcceptedTaxon());
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
	@Override
427
    protected Object getDbIdCdmWithExceptions(CdmBase cdmBase, PesiExportState state) {
428
		if (cdmBase.isInstanceOf(TaxonName.class)){
429
			return ( cdmBase.getId() + state.getConfig().getNameIdStart() );
430
		}if (isAdditionalSource(cdmBase) ){
431
			return ( cdmBase.getId() + 2 * state.getConfig().getNameIdStart() );  //make it a separate variable if conflicts occur.
432
		}else{
433
			return super.getDbIdCdmWithExceptions(cdmBase, state);
434
		}
435
	}
436

    
437

    
438

    
439
	private boolean isAdditionalSource(CdmBase cdmBase) {
440
		if (cdmBase.isInstanceOf(TextData.class)){
441
			TextData textData = CdmBase.deproxy(cdmBase, TextData.class);
442
			if (textData.getFeature().equals(Feature.ADDITIONAL_PUBLICATION()) ||
443
					textData.getFeature().equals(Feature.CITATION())){
444
				return true;
445
			}
446
		}
447
		return false;
448
	}
449

    
450

    
451
	protected MarkerType getUuidMarkerType(UUID uuid, PesiExportState state){
452
		if (uuid == null){
453
			uuid = UUID.randomUUID();
454
		}
455

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

    
467
			state.putMarkerType(markerType);
468
			return markerType;
469
		}
470

    
471

    
472

    
473
	protected static TaxonNameDefaultCacheStrategy getCacheStrategy(TaxonName taxonName) {
474
	    TaxonNameDefaultCacheStrategy cacheStrategy;
475
		if (taxonName.isZoological()){
476
			cacheStrategy = zooNameStrategy;
477
		}else if (taxonName.isBotanical()) {
478
			cacheStrategy = botanicalNameStrategy;
479
		}else{
480
			logger.error("Unhandled taxon name type. Can't define strategy class");
481
			cacheStrategy = botanicalNameStrategy;
482
		}
483
		return cacheStrategy;
484
	}
485

    
486

    
487

    
488

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

    
497
	}
498

    
499

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

    
519

    
520

    
521

    
522

    
523

    
524

    
525

    
526

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

    
540
}
(4-4/12)