Project

General

Profile

Download (16.5 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 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

    
10
package eu.etaxonomy.cdm.model.name;
11

    
12
import java.util.HashSet;
13
import java.util.Set;
14

    
15
import javax.persistence.Entity;
16
import javax.persistence.FetchType;
17
import javax.persistence.OneToMany;
18
import javax.persistence.Transient;
19
import javax.xml.bind.annotation.XmlAccessType;
20
import javax.xml.bind.annotation.XmlAccessorType;
21
import javax.xml.bind.annotation.XmlElement;
22
import javax.xml.bind.annotation.XmlElementWrapper;
23
import javax.xml.bind.annotation.XmlIDREF;
24
import javax.xml.bind.annotation.XmlSchemaType;
25
import javax.xml.bind.annotation.XmlType;
26

    
27
import org.apache.log4j.Logger;
28
import org.hibernate.envers.Audited;
29

    
30
import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
31
import eu.etaxonomy.cdm.model.reference.Reference;
32

    
33

    
34
/**
35
 * The homotypical group class represents a set of {@link TaxonNameBase taxon names} associated
36
 * on the base of their typifications. Since it can be asserted that two taxon
37
 * names are typified by the same type without mentioning the type itself, even
38
 * taxon names without explicit {@link TypeDesignationBase type designation} can belong
39
 * to an homotypical group.<BR>
40
 * Taxon names belonging to an homotypical group and the taxon names or
41
 * {@link eu.etaxonomy.cdm.model.occurrence.DerivedUnit specimens} used as types for their
42
 * {@link TypeDesignationBase type designations} have the following properties: <ul>
43
 * <li>	A taxon name belongs exactly to one homotypical group
44
 * <li>	A type specimen or a type name can be used as a type only for taxon
45
 * 		names belonging to the same homotypical group<BR>
46
 * 		- therefore an homotypical group circumscribes a set of types<BR>
47
 * 		- each taxon name shares a subset of these types<BR>
48
 * 		- each type is used by a subset of these taxon names
49
 * 			within the homotypical group
50
 * <li>	Names that share at least one common type must belong to the same
51
 * 		homotypical group
52
 * <li>	Names that share the same basionym or replaced synonym must belong to
53
 * 		the same homotypical group
54
 * </ul>
55
 *
56
 * @see		TypeDesignationBase
57
 * @see		NameTypeDesignation
58
 * @see		SpecimenTypeDesignation
59
 * @author  m.doering
60
 * @version 1.0
61
 * @created 08-Nov-2007
62
 */
63
@XmlAccessorType(XmlAccessType.FIELD)
64
@XmlType(name = "HomotypicalGroup", propOrder = {
65
    "typifiedNames"
66
})
67
@Entity
68
@Audited
69
public class HomotypicalGroup extends AnnotatableEntity {
70
	private static final long serialVersionUID = -2308347613205551766L;
71

    
72
	private static final Logger logger = Logger.getLogger(HomotypicalGroup.class);
73

    
74
	@XmlElementWrapper(name = "TypifiedNames")
75
	@XmlElement(name = "TypifiedName")
76
	@XmlIDREF
77
	@XmlSchemaType(name = "IDREF")
78
	@OneToMany(mappedBy="homotypicalGroup", fetch=FetchType.LAZY)
79
	protected Set<TaxonNameBase> typifiedNames = new HashSet<TaxonNameBase>();
80

    
81
// ******************** static methods **************************************/
82
	/**
83
	 * Creates a new homotypical group instance with an empty set of typified
84
	 * {@link TaxonNameBase taxon names}.
85
	 *
86
	 * @see #HomotypicalGroup()
87
	 */
88
	public static HomotypicalGroup NewInstance(){
89
		return new HomotypicalGroup();
90
	}
91

    
92

    
93
//********************** CONSTRUCTOR ********************************************/
94

    
95
	/**
96
	 * Class constructor: creates a new homotypical group instance with an
97
	 * empty set of typified {@link TaxonNameBase taxon names}.
98
	 */
99
	protected HomotypicalGroup() {
100
		super();
101
	}
102

    
103
// ********************** GETTER/SETTER/ADDER/REMOVER ********************************/
104

    
105
	/**
106
	 * Returns the set of {@link TaxonNameBase taxon names} that belong to <i>this</i> homotypical group.
107
	 *
108
	 * @see	#getSpecimenTypeDesignations()
109
	 */
110
	public Set<TaxonNameBase> getTypifiedNames() {
111
		return typifiedNames;
112
	}
113

    
114
	/**
115
	 * Adds a new {@link TaxonNameBase taxon name} to the set of taxon names that belong
116
	 * to <i>this</i> homotypical group.
117
	 *
118
	 * @param  typifiedName  the taxon name to be added to <i>this</i> group
119
	 * @see 			  	 #getTypifiedNames()
120
	 * @see 			  	 #removeTypifiedName(TaxonNameBase)
121
	 */
122
	public void addTypifiedName(TaxonNameBase typifiedName) {
123
		if (typifiedName != null){
124
			typifiedNames.add(typifiedName);
125
			//if (typifiedName.getHomotypicalGroup() != null && !typifiedName.getHomotypicalGroup().equals(this))
126
			typifiedName.setHomotypicalGroup(this);
127
		}
128
	}
129

    
130
	/**
131
	 * @see #removeTypifiedName(TaxonNameBase, boolean)
132
	 * @param typifiedName
133
	 */
134
	public void removeTypifiedName(TaxonNameBase typifiedName) {
135
		removeTypifiedName(typifiedName, false);
136
	}
137

    
138

    
139
	/**
140
	 * Removes one element from the set of {@link TaxonNameBase taxon names}
141
	 * that belong to <i>this</i> homotypical group.
142
	 *
143
	 * @param  typifiedName	the taxon name which should be removed from the corresponding set
144
	 * @param  removeGroup  if <code>true</code> the typified name is given a new
145
	 * 						homotypical group
146
	 * @see    #addTypifiedName(TaxonNameBase)
147
	 */
148
	public void removeTypifiedName(TaxonNameBase typifiedName, boolean removeGroup) {
149
		if (removeGroup){
150
			HomotypicalGroup newHomotypicalGroup = HomotypicalGroup.NewInstance();
151
			typifiedName.setHomotypicalGroup(newHomotypicalGroup);
152
		}
153

    
154
		typifiedNames.remove(typifiedName);
155
	}
156

    
157
	/**
158
	 * Merges the typified {@link TaxonNameBase taxon names} from one homotypical group into
159
	 * the set of typified taxon names of <i>this</i> homotypical group.
160
	 *
161
	 * @param	homotypicalGroupToMerge the homotypical group the typified names of which
162
	 * 									are to be transferred to <i>this</i> homotypical group
163
	 */
164
	public void merge(HomotypicalGroup homotypicalGroupToMerge){
165
		if (homotypicalGroupToMerge != null){
166
			Set<TaxonNameBase> typifiedNames = new HashSet<TaxonNameBase>();
167
			typifiedNames.addAll(homotypicalGroupToMerge.getTypifiedNames());
168
			for (TaxonNameBase typifiedName: typifiedNames){
169
				this.addTypifiedName(typifiedName);
170
			}
171
		}
172
	}
173

    
174

    
175
	/**
176
	 * Returns the set of {@link SpecimenTypeDesignation specimen type designations} that
177
	 * typify the {@link TaxonNameBase taxon names} belonging to <i>this</i> homotypical group
178
	 * including the status of these designations.
179
	 *
180
	 * @see	#getTypifiedNames()
181
	 * @see	#getNameTypeDesignations()
182
	 * @see	#getTypeDesignations()
183
	 * @see	TaxonNameBase#getSpecimenTypeDesignations()
184
	 */
185
	@Transient
186
	public Set<SpecimenTypeDesignation> getSpecimenTypeDesignations(){
187
		Set<SpecimenTypeDesignation> result = new HashSet<SpecimenTypeDesignation>();
188
		for (TaxonNameBase taxonName : typifiedNames){
189
			result.addAll(taxonName.getSpecimenTypeDesignations());
190
		}
191
		return result;
192
	}
193

    
194
	/**
195
	 * Returns the set of {@link NameTypeDesignation name type designations} that
196
	 * typify the {@link TaxonNameBase taxon names} belonging to <i>this</i> homotypical group
197
	 * including the status of these designations.
198
	 *
199
	 * @see	#getTypifiedNames()
200
	 * @see	#getSpecimenTypeDesignations()
201
	 * @see	#getTypeDesignations()
202
	 * @see	TaxonNameBase#getNameTypeDesignations()
203
	 */
204
	@Transient
205
	public Set<NameTypeDesignation> getNameTypeDesignations(){
206
		Set<NameTypeDesignation> result = new HashSet<NameTypeDesignation>();
207
		for (TaxonNameBase taxonName : typifiedNames){
208
			result.addAll(taxonName.getNameTypeDesignations());
209
		}
210
		return result;
211
	}
212

    
213

    
214
	/**
215
	 * Returns the set of all {@link TypeDesignationBase type designations} that
216
	 * typify the {@link TaxonNameBase taxon names} belonging to <i>this</i> homotypical group
217
	 * (this includes either {@link NameTypeDesignation name type designations} or
218
	 * {@link SpecimenTypeDesignation specimen type designations}).
219
	 *
220
	 * @see	#getTypifiedNames()
221
	 * @see	#getNameTypeDesignations()
222
	 * @see	#getSpecimenTypeDesignations()
223
	 * @see	TaxonNameBase#getTypeDesignations()
224
	 */
225
	@Transient
226
	public Set<TypeDesignationBase> getTypeDesignations(){
227
		Set<TypeDesignationBase> result = new HashSet<TypeDesignationBase>();
228
		for (TaxonNameBase taxonName : typifiedNames){
229
			result.addAll(taxonName.getTypeDesignations());
230
		}
231
		return result;
232
	}
233

    
234

    
235
    /**
236
     * Creates a basionym relationship to all other names in this names homotypical
237
     * group.
238
     *
239
     * @see HomotypicalGroup.setGroupBasionym(TaxonNameBase basionymName)
240
     *
241
     * @param basionymName
242
     * @throws IllegalArgumentException if basionymName is not member in this homotypical group
243
     */
244
	@Transient
245
	public void setGroupBasionym(TaxonNameBase basionymName) throws IllegalArgumentException{
246
    	setGroupBasionym(basionymName, null, null, null);
247
    }
248

    
249
	public void setGroupBasionym(TaxonNameBase basionymName, Reference citation, String microCitation, String ruleConsidered)
250
    			throws IllegalArgumentException {
251
    	if (! typifiedNames.contains(basionymName)){
252
        	throw new IllegalArgumentException("Name to be set as basionym/original combination must be part of the homotypical group but is not");
253
        }
254
        if (typifiedNames.size() < 2){return;}
255
//
256
    	//Add new relations
257
        Set<TaxonNameBase> typified = new HashSet<TaxonNameBase>();
258
        for (TaxonNameBase name : typifiedNames){
259
        	typified.add(name);
260
        }
261
        for (TaxonNameBase name : typified) {
262
    		if (!name.equals(basionymName)) {
263
		    	name.addRelationshipFromName(basionymName, NameRelationshipType.BASIONYM(), citation, microCitation, ruleConsidered);
264
			}
265
    	}
266
        typifiedNames= typified;
267
    }
268

    
269

    
270

    
271
    /**
272
     * Removes all basionym relationships between basionymName and any other name
273
     * in it's homotypic group
274
     *
275
     * @param basionymName
276
     */
277
     public static void removeGroupBasionym(TaxonNameBase basionymName) {
278
    	 HomotypicalGroup homotypicalGroup = basionymName.getHomotypicalGroup();
279
         Set<NameRelationship> relations = basionymName.getRelationsFromThisName();
280
         Set<NameRelationship> removeRelations = new HashSet<NameRelationship>();
281

    
282
         for (NameRelationship relation : relations) {
283

    
284
                 // If this is a basionym relation, and toName is in the homotypical group,
285
                 //      remove the relationship.
286
                 if (relation.getType().isBasionymRelation() &&
287
                                 relation.getToName().getHomotypicalGroup().equals(homotypicalGroup)) {
288
                         removeRelations.add(relation);
289
                 }
290
         }
291

    
292
          // Removing relations from a set through which we are iterating causes a
293
          //      ConcurrentModificationException. Therefore, we delete the targeted
294
          //      relations in a second step.
295
          for (NameRelationship relation : removeRelations) {
296
                  basionymName.removeNameRelationship(relation);
297
          }
298
     }
299

    
300
//     /**
301
//     * TODO
302
//     * This method tries to set basionym(s) for a homotypical group
303
//     * Rule: Basionym must not have basionym author and not protectedAuthorshipCache
304
//     *       Others must have basionymAuthor
305
//     *       BasionymAuhtors must have equal NomenclaturalTitleCache to basionym
306
//       *     last epithet must fit (be equal except for ending), otherwise we expect it to be replaced synonym
307
//       *     those names fitting to a basionym relationship must remove all other basionym (and replaced synonym?) relationships
308
//       * Open: All types of relationships to replaced synonyms
309
//     */
310
//    public void guessAndSetBasionym(){
311
//         Map<String, INonViralName> candidates = new HashMap<>();
312
//         for (TaxonNameBase<?,?> typifiedName : this.typifiedNames){
313
//             if (! typifiedName.protectedAuthorshipCache && nvn.getBasionymAuthorship() == null){
314
//                 candidates.add(typifiedName);
315
//             }
316
//         }
317
//         if (candidates.size() == 1){
318
//             for (TaxonNameBase<?,?> typifiedName : this.typifiedNames){
319
//                 removeGroupBasionym(typifiedName);
320
//             }
321
//             this.setGroupBasionym(candidates.iterator().next());
322
//         }
323
//
324
//     }
325

    
326

    
327
	/**
328
	 * Returns all taxon names in the homotypic group that do not have an
329
	 * 'is_basionym_for' (zool.: 'is_original_combination_for')
330
	 * or a replaced synonym relationship.
331
	 * @return
332
	 */
333
	@Transient
334
	public Set<TaxonNameBase> getUnrelatedNames(){
335
		Set<NameRelationship> set = getBasionymOrReplacedSynonymRelations(true, true);
336
		Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
337
		result.addAll(this.getTypifiedNames());
338
		for (NameRelationship nameRelationship : set){
339
			result.remove(nameRelationship.getFromName());
340
			result.remove(nameRelationship.getToName());
341
		}
342
		return result;
343
	}
344

    
345
	/**
346
	 * Returns all taxon names in the homotypical group that are new combinations (have a basionym/original combination
347
	 * or a replaced synonym).
348
	 * @return
349
	 */
350
	@Transient
351
	public Set<TaxonNameBase> getNewCombinations(){
352
		Set<NameRelationship> set = getBasionymOrReplacedSynonymRelations(true, true);
353
		Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
354
		for (NameRelationship nameRelationship : set){
355
			result.add(nameRelationship.getToName());
356
		}
357
		return result;
358
	}
359

    
360

    
361

    
362
	/**
363
	 * Returns all taxon names in the homotypical group that have an 'is_basionym_for' (zool.: 'is_original_combination_for')
364
	 * or a replaced synonym relationship.
365
	 * @return
366
	 */
367
	@Transient
368
	public Set<TaxonNameBase> getBasionymsOrReplacedSynonyms(){
369
		Set<NameRelationship> set = getBasionymOrReplacedSynonymRelations(true, true);
370
		Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
371
		for (NameRelationship nameRelationship : set){
372
			result.add(nameRelationship.getFromName());
373
		}
374
		return result;
375
	}
376

    
377
	/**
378
	 * Returns all taxon names in the homotypical group that have a 'is_basionym_for' (zool.: 'is_original_combination_for') relationship.
379
	 * @return
380
	 */
381
	@Transient
382
	public Set<TaxonNameBase> getBasionyms(){
383
		Set<NameRelationship> set = getBasionymOrReplacedSynonymRelations(true, false);
384
		Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
385
		for (NameRelationship nameRelationship : set){
386
			result.add(nameRelationship.getFromName());
387
		}
388
		return result;
389
	}
390

    
391
	/**
392
	 * Returns all taxon names in the homotypical group that have a 'is_replaced_synonym_for' relationship.
393
	 * @return
394
	 */
395
	@Transient
396
	public Set<TaxonNameBase> getReplacedSynonym(){
397
		Set<NameRelationship> set = getBasionymOrReplacedSynonymRelations(false, true);
398
		Set<TaxonNameBase> result = new HashSet<TaxonNameBase>();
399
		for (NameRelationship nameRelationship : set){
400
			result.add(nameRelationship.getFromName());
401
		}
402
		return result;
403
	}
404

    
405
	/**
406
	 * Returns the name relationships that represent either a basionym (original combination) relationship or
407
	 * a replaced synonym relationship.
408
	 * @return
409
	 */
410
	@Transient
411
	public Set<NameRelationship> getBasionymAndReplacedSynonymRelations(){
412
		return getBasionymOrReplacedSynonymRelations(true, true);
413
	}
414

    
415
	/**
416
	 * Computes all basionym and replaced synonym relationships between names in this group.
417
	 * If <code>doBasionym</code> is <code>false</code> basionym relationships are excluded.
418
	 * If <code>doReplacedSynonym</code> is <code>false</code> replaced synonym relationships are excluded.
419
	 * @param doBasionym
420
	 * @param doReplacedSynonym
421
	 * @return
422
	 */
423
	@Transient
424
	private Set<NameRelationship> getBasionymOrReplacedSynonymRelations(boolean doBasionym, boolean doReplacedSynonym){
425
		Set<NameRelationship> result = new HashSet<NameRelationship>();
426
		Set<TaxonNameBase> names = this.getTypifiedNames();
427
		if (names.size() > 1){
428
			for (TaxonNameBase name : names){
429
				Set nameRels = name.getNameRelations();
430
				//TODO make getNameRelations generic
431
				for (Object obj : nameRels){
432
					NameRelationship nameRel = (NameRelationship)obj;
433
					NameRelationshipType type = nameRel.getType();
434
					if ( type.isBasionymRelation() && doBasionym){
435
						if (testRelatedNameInThisGroup(nameRel)){
436
							result.add(nameRel);
437
						}else{
438
							logger.warn("Name has basionym relation to a name that is not in the same homotypical group");
439
						}
440
					}else if (type.isReplacedSynonymRelation() && doReplacedSynonym)  {
441
						if (testRelatedNameInThisGroup(nameRel)){
442
							result.add(nameRel);
443
						}else{
444
							logger.warn("Name has replaced synonym relation to a name that is not in the same homotypical group");
445
						}
446
					}
447
				}
448
			}
449
		}
450
		return result;
451
	}
452

    
453
	private boolean testRelatedNameInThisGroup(NameRelationship nameRel){
454
		TaxonNameBase toName = nameRel.getToName();
455
		return (this.getTypifiedNames().contains(toName));
456
	}
457

    
458
	private boolean isBasionymOrRepSynRel(NameRelationshipType relType){
459
		if (relType == null){
460
			throw new IllegalArgumentException("NameRelationshipType should never be null");
461
		}else if (relType.equals(NameRelationshipType.BASIONYM())) {
462
			return true;
463
		}else if (relType.equals(NameRelationshipType.REPLACED_SYNONYM())){
464
			return true;
465
		}else{
466
			return false;
467
		}
468
	}
469

    
470

    
471
}
(4-4/42)