Project

General

Profile

Download (15.4 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.taxon;
11

    
12
import java.lang.reflect.Method;
13
import java.util.List;
14

    
15
import javax.persistence.Column;
16
import javax.persistence.Entity;
17
import javax.persistence.FetchType;
18
import javax.persistence.ManyToOne;
19
import javax.persistence.Transient;
20
import javax.validation.constraints.NotNull;
21
import javax.xml.bind.annotation.XmlAccessType;
22
import javax.xml.bind.annotation.XmlAccessorType;
23
import javax.xml.bind.annotation.XmlAttribute;
24
import javax.xml.bind.annotation.XmlElement;
25
import javax.xml.bind.annotation.XmlIDREF;
26
import javax.xml.bind.annotation.XmlSchemaType;
27
import javax.xml.bind.annotation.XmlType;
28

    
29
import org.apache.log4j.Logger;
30
import org.hibernate.annotations.Cascade;
31
import org.hibernate.annotations.CascadeType;
32
import org.hibernate.annotations.Index;
33
import org.hibernate.annotations.Table;
34
import org.hibernate.envers.Audited;
35
import org.hibernate.search.annotations.ClassBridge;
36
import org.hibernate.search.annotations.ClassBridges;
37
import org.hibernate.search.annotations.IndexedEmbedded;
38
import org.hibernate.search.annotations.Store;
39

    
40
import eu.etaxonomy.cdm.common.CdmUtils;
41
import eu.etaxonomy.cdm.hibernate.search.AcceptedTaxonBridge;
42
import eu.etaxonomy.cdm.hibernate.search.ClassInfoBridge;
43
import eu.etaxonomy.cdm.model.common.IIntextReferenceTarget;
44
import eu.etaxonomy.cdm.model.common.IPublishable;
45
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
46
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
47
import eu.etaxonomy.cdm.model.name.ITaxonNameBase;
48
import eu.etaxonomy.cdm.model.name.Rank;
49
import eu.etaxonomy.cdm.model.name.TaxonName;
50
import eu.etaxonomy.cdm.model.reference.Reference;
51
import eu.etaxonomy.cdm.strategy.cache.TaggedText;
52
import eu.etaxonomy.cdm.strategy.cache.name.CacheUpdate;
53
import eu.etaxonomy.cdm.strategy.cache.taxon.ITaxonCacheStrategy;
54
import eu.etaxonomy.cdm.validation.Level2;
55
import eu.etaxonomy.cdm.validation.Level3;
56
import eu.etaxonomy.cdm.validation.annotation.NullOrNotEmpty;
57
import eu.etaxonomy.cdm.validation.annotation.TaxonNameCannotBeAcceptedAndSynonym;
58

    
59
/**
60
 * The upmost (abstract) class for the use of a {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} in a {@link eu.etaxonomy.cdm.model.reference.Reference reference}
61
 * or within a taxonomic view/treatment either as a {@link Taxon taxon}
62
 * ("accepted" respectively "correct" name) or as a (junior) {@link Synonym synonym}.
63
 * Within a taxonomic view/treatment or a reference a taxon name can be used
64
 * only in one of both described meanings. The reference using the taxon name
65
 * is generally cited with "sec." (secundum, sensu). For instance:
66
 * "<i>Juncus longirostris</i> Kuvaev sec. Kirschner, J. et al. 2002".
67
 * <P>
68
 * This class corresponds to: <ul>
69
 * <li> TaxonConcept according to the TDWG ontology
70
 * <li> TaxonConcept according to the TCS
71
 * </ul>
72
 *
73
 * @author m.doering
74
 * @created 08-Nov-2007 13:06:56
75
 */
76
@XmlAccessorType(XmlAccessType.FIELD)
77
@XmlType(name = "TaxonBase", propOrder = {
78
    "name",
79
    "sec",
80
    "doubtful",
81
    "secMicroReference",
82
    "appendedPhrase",
83
    "useNameCache",
84
    "publish"
85
})
86
@Entity
87
@Audited
88
//@PreFilter("hasPermission(filterObject, 'edit')")
89
@Table(appliesTo="TaxonBase", indexes = { @Index(name = "taxonBaseTitleCacheIndex", columnNames = { "titleCache" }) })
90
@TaxonNameCannotBeAcceptedAndSynonym(groups = Level3.class)
91
@ClassBridges({
92
    @ClassBridge(name="classInfo",
93
            index = org.hibernate.search.annotations.Index.YES,
94
            store = Store.YES,
95
            impl = ClassInfoBridge.class),
96
    @ClassBridge(name="accTaxon", // TODO rename to acceptedTaxon, since we are usually not using abbreviations for field names
97
            index = org.hibernate.search.annotations.Index.YES,
98
            store = Store.YES,
99
            impl = AcceptedTaxonBridge.class),
100
    @ClassBridge(impl = eu.etaxonomy.cdm.hibernate.search.NomenclaturalSortOrderBrigde.class)
101
})
102
public abstract class TaxonBase<S extends ITaxonCacheStrategy> extends IdentifiableEntity<S> implements  IPublishable, IIntextReferenceTarget, Cloneable {
103
    private static final long serialVersionUID = -3589185949928938529L;
104
    private static final Logger logger = Logger.getLogger(TaxonBase.class);
105

    
106
    private static Method methodTaxonNameAddTaxonBase;
107

    
108
    static {
109
        try {
110
            methodTaxonNameAddTaxonBase = TaxonName.class.getDeclaredMethod("addTaxonBase", TaxonBase.class);
111
            methodTaxonNameAddTaxonBase.setAccessible(true);
112
        } catch (Exception e) {
113
            logger.error(e);
114
            for(StackTraceElement ste : e.getStackTrace()) {
115
                logger.error(ste);
116
            }
117
        }
118
    }
119

    
120
    //The assignment to the Taxon or to the Synonym class is not definitive
121
    @XmlAttribute(name = "isDoubtful")
122
    private boolean doubtful;
123

    
124
    @XmlElement(name = "Name")
125
    @XmlIDREF
126
    @XmlSchemaType(name = "IDREF")
127
    @ManyToOne(fetch = FetchType.LAZY)
128
    @IndexedEmbedded(includeEmbeddedObjectId=true)
129
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
130
    @NotNull(groups = Level2.class)
131
    private TaxonName<?,?> name;
132

    
133
    // The concept reference
134
    @XmlElement(name = "Sec")
135
    @XmlIDREF
136
    @XmlSchemaType(name = "IDREF")
137
    @ManyToOne(fetch = FetchType.LAZY)
138
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
139
    @NotNull(groups = Level2.class)
140
    @IndexedEmbedded
141
    private Reference sec;
142

    
143
    @XmlElement(name = "secMicroReference")
144
    @CacheUpdate(noUpdate ="titleCache")
145
    @NullOrNotEmpty
146
    @Column(length=255)
147
    private String secMicroReference;
148

    
149
    @XmlElement(name = "AppendedPhrase")
150
    private String appendedPhrase;
151

    
152
    @XmlAttribute(name= "UseNameCache")
153
    private boolean useNameCache = false;
154

    
155
    @XmlAttribute(name = "publish")
156
    private boolean publish = true;
157

    
158

    
159
// ************* CONSTRUCTORS *************/
160
    /**
161
     * Class constructor: creates a new empty (abstract) taxon.
162
     *
163
     * @see 	#TaxonBase(TaxonName, Reference)
164
     */
165
    protected TaxonBase(){
166
        super();
167
    }
168

    
169
    /**
170
     * Class constructor: creates a new (abstract) taxon with the
171
     * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
172
     * using it.
173
     *
174
     * @param  taxonName	the taxon name used
175
     * @param  sec				the reference using the taxon name
176
     * @see    #TaxonBase()
177
     */
178
    protected TaxonBase(TaxonName taxonName, Reference sec, String secDetail){
179
        super();
180
        if (taxonName != null){
181
            this.invokeSetMethod(methodTaxonNameAddTaxonBase, taxonName);
182
        }
183
        this.setSec(sec);
184
        this.setSecMicroReference(secDetail);
185
    }
186

    
187
//********* METHODS **************************************/
188

    
189
    /**
190
     * Generates and returns the string with the full scientific name (including
191
     * authorship) of the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used in <i>this</i>
192
     * (abstract) taxon as well as the title of the {@link eu.etaxonomy.cdm.model.reference.Reference reference} using
193
     * this taxon name. This string may be stored in the inherited
194
     * {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache() titleCache} attribute.
195
     * This method overrides the generic and inherited generateTitle() method
196
     * from {@link eu.etaxonomy.cdm.model.common.IdentifiableEntity IdentifiableEntity}.
197
     *
198
     * @return  the string with the full scientific name of the taxon name
199
     *			and with the title of the reference involved in <i>this</i> (abstract) taxon
200
     * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#generateTitle()
201
     * @see  	eu.etaxonomy.cdm.model.common.IdentifiableEntity#getTitleCache()
202
     */
203
//	@Override
204
//	public String generateTitle() {
205
//		String title;
206
//		if (name != null && name.getTitleCache() != null){
207
//			title = name.getTitleCache() + " sec. ";
208
//			if (sec != null){
209
//				title += sec.getTitleCache();
210
//			}else{
211
//				title += "???";
212
//			}
213
//		}else{
214
//			title = this.toString();
215
//		}
216
//		return title;
217
//	}
218

    
219
    @Transient
220
    public List<TaggedText> getTaggedTitle(){
221
        return getCacheStrategy().getTaggedTitle(this);
222
    }
223

    
224

    
225

    
226
    /**
227
     * Returns the {@link TaxonName taxon name} used in <i>this</i> (abstract) taxon.
228
     */
229
    public TaxonName getName(){
230
        return this.name;
231
    }
232

    
233
    public void setName(TaxonName name) {
234
        if (this.name != null){
235
            this.name.getTaxonBases().remove(this);
236
        }
237
        if(name != null) {
238
            name.getTaxonBases().add(this);
239
        }
240
        this.name = name;
241
    }
242

    
243
    /**
244
     * Returns the {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group} of the
245
     * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used in <i>this</i> (abstract) taxon.
246
     */
247
    @Transient
248
    public HomotypicalGroup getHomotypicGroup(){
249
        if (this.getName() == null){
250
            return null;
251
        }else{
252
            return this.getName().getHomotypicalGroup();
253
        }
254
    }
255

    
256
    /**
257
     * Returns the boolean value indicating whether the assignment of <i>this</i>
258
     * (abstract) taxon to the {@link Taxon Taxon} or to the {@link Synonym Synonym} class
259
     * is definitive (false) or not (true). If this flag is set the use of <i>this</i> (abstract)
260
     * taxon as an "accepted/correct" name or as a (junior) "synonym" might
261
     * still change in the course of taxonomical working process.
262
     */
263
    public boolean isDoubtful(){
264
        return this.doubtful;
265
    }
266
    /**
267
     * @see  #isDoubtful()
268
     */
269
    public void setDoubtful(boolean doubtful){
270
        this.doubtful = doubtful;
271
    }
272

    
273

    
274
    /**
275
     * Returns the boolean value indicating if this taxon should be withheld (<code>publish=false</code>) or not
276
     * (<code>publish=true</code>) during any publication process to the general public.
277
     * This publish flag implementation is preliminary and may be replaced by a more general
278
     * implementation of READ rights in future.<BR>
279
     * The default value is <code>true</code>.
280
     */
281
    @Override
282
    public boolean isPublish() {
283
        return publish;
284
    }
285

    
286
    @Override
287
    public void setPublish(boolean publish) {
288
        this.publish = publish;
289
    }
290

    
291
    /**
292
     * Returns the {@link eu.etaxonomy.cdm.model.reference.Reference reference} of <i>this</i> (abstract) taxon.
293
     * This is the reference or the treatment using the {@link TaxonName taxon name}
294
     * in <i>this</i> (abstract) taxon.
295
     */
296
    public Reference getSec() {
297
        return sec;
298
    }
299
    /**
300
     * @see  #getSec()
301
     */
302
    public void setSec(Reference sec) {
303
        this.sec = sec;
304
    }
305

    
306
    /**
307
     * @return the micro reference (detail) for the sec(undum)
308
     * reference
309
     * @see #getSec()
310
     */
311
    public String getSecMicroReference() {
312
        return secMicroReference;
313
    }
314

    
315
    /**
316
     * @see #getSecMicroReference()
317
     * @see #getSec()
318
     */
319
    public void setSecMicroReference(String secMicroReference) {
320
        this.secMicroReference = CdmUtils.Nb(secMicroReference);
321
    }
322

    
323

    
324

    
325
    /**
326
     * An appended phrase is a phrase that is added to the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name}
327
     * 's title cache to be used just in this taxon. E.g. the phrase "sensu latu" may be added
328
     * to the name to describe this taxon more precisely.
329
     * If {@link #isUseNameCache()}
330
     * @return the appendedPhrase
331
     */
332
    public String getAppendedPhrase() {
333
        return appendedPhrase;
334
    }
335

    
336
    /**
337
     * @param appendedPhrase the appendedPhrase to set
338
     */
339
    public void setAppendedPhrase(String appendedPhrase) {
340
        this.appendedPhrase = appendedPhrase;
341
    }
342

    
343
    /**
344
     * @return the useNameCache
345
     */
346
    public boolean isUseNameCache() {
347
        return useNameCache;
348
    }
349

    
350
    /**
351
     * @param useNameCache the useNameCache to set
352
     */
353
    public void setUseNameCache(boolean useNameCache) {
354
        this.useNameCache = useNameCache;
355
    }
356

    
357
    /**
358
     * Returns <code>true</code> if <code>this</code>
359
     * taxon base is not part of any classification.
360
     * False otherwise
361
     * @return boolean
362
     */
363
    @Transient
364
    public abstract boolean isOrphaned();
365

    
366

    
367
    /**
368
     * @return
369
     */
370
    @Transient
371
    public Rank getNullSafeRank() {
372
        return name == null ? null : name.getRank();
373
    }
374

    
375
    /**
376
     * This method compares 2 taxa on it's name titles and caches.
377
     * If both are equal it compares on the secundum titleCache as well.
378
     * It is not fully clear/defined how this method relates to
379
     * explicit comparators like {@link TaxonComparator}. The later
380
     * currently uses this method.
381
     * Historically it was a compareTo method in {@link IdentifiableEntity}
382
     * but did not fulfill the {@link Comparable} contract.
383
     * <BR><BR>
384
     * {@link  https://dev.e-taxonomy.eu/redmine/issues/922}<BR>
385
     * {@link https://dev.e-taxonomy.eu/redmine/issues/6311}
386
     *
387
     * @see ITaxonNameBase#compareToName(TaxonName)
388
     * @see TaxonComparator
389
     * @see TaxonNaturalComparator
390
     * @see TaxonNodeByNameComparator
391
     * @see TaxonNodeByRankAndNameComparator
392
     * @param otherTaxon
393
     * @return the compareTo result similar to {@link Comparable#compareTo(Object)}
394
     * @throws NullPointerException if otherTaxon is <code>null</code>
395
     */
396
    //TODO handling of protected titleCache
397
    public int compareToTaxon(TaxonBase otherTaxon){
398

    
399
        int result = 0;
400

    
401
        if (otherTaxon == null) {
402
            throw new NullPointerException("Cannot compare to null.");
403
        }
404

    
405
        otherTaxon = deproxy(otherTaxon);
406

    
407
        TaxonName<?,?> otherName = deproxy(otherTaxon.getName());
408
        ITaxonNameBase thisName = this.getName();
409
        if ((otherName == null || thisName == null)){
410
            if (otherName != thisName){
411
                result = thisName == null ? -1 : 1;
412
            }
413
        }else{
414
            result = thisName.compareToName(otherName);
415
        }
416

    
417
        if (result == 0){
418
            String otherReferenceTitleCache = "";
419
            String thisReferenceTitleCache = "";
420

    
421
            Reference otherRef = deproxy(otherTaxon.getSec());
422
            if (otherRef != null) {
423
                otherReferenceTitleCache = otherRef.getTitleCache();
424
            }
425
            Reference thisRef = deproxy(this.getSec());
426
            if (thisRef != null) {
427
                thisReferenceTitleCache = thisRef.getTitleCache();
428
            }
429
            if ((CdmUtils.isNotBlank(otherReferenceTitleCache) || CdmUtils.isNotBlank(thisReferenceTitleCache))) {
430
                result = thisReferenceTitleCache.compareTo(otherReferenceTitleCache);
431
            }
432
        }
433

    
434
        return result;
435
    }
436

    
437
//*********************** CLONE ********************************************************/
438

    
439
    /**
440
     * Clones <i>this</i> taxon. This is a shortcut that enables to create
441
     * a new instance with empty taxon name and sec reference.
442
     *
443
     * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
444
     * @see java.lang.Object#clone()
445
     */
446
    @Override
447
    public Object clone() {
448
        TaxonBase<?> result;
449
        try {
450
            result = (TaxonBase<?>)super.clone();
451
            result.setSec(null);
452

    
453
            return result;
454
        } catch (CloneNotSupportedException e) {
455
            logger.warn("Object does not implement cloneable");
456
            e.printStackTrace();
457
            return null;
458
        }
459

    
460

    
461
    }
462

    
463

    
464
}
(10-10/20)