Project

General

Profile

Download (15.8 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.Index;
19
import javax.persistence.ManyToOne;
20
import javax.persistence.Table;
21
import javax.persistence.Transient;
22
import javax.validation.constraints.NotNull;
23
import javax.xml.bind.annotation.XmlAccessType;
24
import javax.xml.bind.annotation.XmlAccessorType;
25
import javax.xml.bind.annotation.XmlAttribute;
26
import javax.xml.bind.annotation.XmlElement;
27
import javax.xml.bind.annotation.XmlIDREF;
28
import javax.xml.bind.annotation.XmlSchemaType;
29
import javax.xml.bind.annotation.XmlType;
30

    
31
import org.apache.log4j.Logger;
32
import org.hibernate.annotations.Cascade;
33
import org.hibernate.annotations.CascadeType;
34
import org.hibernate.envers.Audited;
35
import org.hibernate.search.annotations.Analyze;
36
import org.hibernate.search.annotations.ClassBridge;
37
import org.hibernate.search.annotations.ClassBridges;
38
import org.hibernate.search.annotations.Field;
39
import org.hibernate.search.annotations.FieldBridge;
40
import org.hibernate.search.annotations.IndexedEmbedded;
41
import org.hibernate.search.annotations.Store;
42
import org.hibernate.search.bridge.builtin.BooleanBridge;
43

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

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

    
110
    private static final long serialVersionUID = -3589185949928938529L;
111
    private static final Logger logger = Logger.getLogger(TaxonBase.class);
112

    
113
    public static final String ACC_TAXON_BRIDGE_PREFIX = "accTaxon";
114

    
115
    private static Method methodTaxonNameAddTaxonBase;
116

    
117
    static {
118
        try {
119
            methodTaxonNameAddTaxonBase = TaxonName.class.getDeclaredMethod("addTaxonBase", TaxonBase.class);
120
            methodTaxonNameAddTaxonBase.setAccessible(true);
121
        } catch (Exception e) {
122
            logger.error(e);
123
            for(StackTraceElement ste : e.getStackTrace()) {
124
                logger.error(ste);
125
            }
126
        }
127
    }
128

    
129
    //The assignment to the Taxon or to the Synonym class is not definitive
130
    @XmlAttribute(name = "isDoubtful")
131
    private boolean doubtful;
132

    
133
    @XmlElement(name = "Name")
134
    @XmlIDREF
135
    @XmlSchemaType(name = "IDREF")
136
    @ManyToOne(fetch = FetchType.LAZY)
137
    @IndexedEmbedded(includeEmbeddedObjectId=true)
138
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
139
    @NotNull(groups = Level2.class)
140
    private TaxonName name;
141

    
142
    // The concept reference
143
    @XmlElement(name = "Sec")
144
    @XmlIDREF
145
    @XmlSchemaType(name = "IDREF")
146
    @ManyToOne(fetch = FetchType.LAZY)
147
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
148
    @NotNull(groups = Level2.class)
149
    @IndexedEmbedded
150
    private Reference sec;
151

    
152
    @XmlElement(name = "secMicroReference")
153
    @CacheUpdate(noUpdate ="titleCache")
154
    @NullOrNotEmpty
155
    @Column(length=255)
156
    private String secMicroReference;
157

    
158
    @XmlElement(name = "AppendedPhrase")
159
    private String appendedPhrase;
160

    
161
    @XmlAttribute(name= "UseNameCache")
162
    private boolean useNameCache = false;
163

    
164
    @XmlAttribute(name = "publish")
165
    @Field(analyze = Analyze.NO, store = Store.YES, bridge= @FieldBridge(impl=BooleanBridge.class))
166
    private boolean publish = true;
167

    
168

    
169
// ************* CONSTRUCTORS *************/
170
    /**
171
     * Class constructor: creates a new empty (abstract) taxon.
172
     *
173
     * @see 	#TaxonBase(TaxonName, Reference)
174
     */
175
    protected TaxonBase(){
176
        super();
177
    }
178

    
179
    /**
180
     * Class constructor: creates a new (abstract) taxon with the
181
     * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used and the {@link eu.etaxonomy.cdm.model.reference.Reference reference}
182
     * using it.
183
     *
184
     * @param  taxonName	the taxon name used
185
     * @param  sec				the reference using the taxon name
186
     * @see    #TaxonBase()
187
     */
188
    protected TaxonBase(TaxonName taxonName, Reference sec, String secDetail){
189
        super();
190
        if (taxonName != null){
191
            this.invokeSetMethod(methodTaxonNameAddTaxonBase, taxonName);
192
        }
193
        this.setSec(sec);
194
        this.setSecMicroReference(secDetail);
195
    }
196

    
197
//********* METHODS **************************************/
198

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

    
229
    @Transient
230
    public List<TaggedText> getTaggedTitle(){
231
        return getCacheStrategy().getTaggedTitle(this);
232
    }
233

    
234

    
235

    
236
    /**
237
     * Returns the {@link TaxonName taxon name} used in <i>this</i> (abstract) taxon.
238
     */
239
    public TaxonName getName(){
240
        return this.name;
241
    }
242

    
243
    public void setName(TaxonName name) {
244
        if (this.name != null){
245
            this.name.getTaxonBases().remove(this);
246
        }
247
        if(name != null) {
248
            name.getTaxonBases().add(this);
249
        }
250
        this.name = name;
251
    }
252

    
253
    /**
254
     * Returns the {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group} of the
255
     * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used in <i>this</i> (abstract) taxon.
256
     */
257
    @Transient
258
    public HomotypicalGroup getHomotypicGroup(){
259
        if (this.getName() == null){
260
            return null;
261
        }else{
262
            return this.getName().getHomotypicalGroup();
263
        }
264
    }
265

    
266
    /**
267
     * Returns the boolean value indicating whether the assignment of <i>this</i>
268
     * (abstract) taxon to the {@link Taxon Taxon} or to the {@link Synonym Synonym} class
269
     * is definitive (false) or not (true). If this flag is set the use of <i>this</i> (abstract)
270
     * taxon as an "accepted/correct" name or as a (junior) "synonym" might
271
     * still change in the course of taxonomical working process.
272
     */
273
    public boolean isDoubtful(){
274
        return this.doubtful;
275
    }
276
    /**
277
     * @see  #isDoubtful()
278
     */
279
    public void setDoubtful(boolean doubtful){
280
        this.doubtful = doubtful;
281
    }
282

    
283

    
284
    /**
285
     * Returns the boolean value indicating if this taxon should be withheld (<code>publish=false</code>) or not
286
     * (<code>publish=true</code>) during any publication process to the general public.
287
     * This publish flag implementation is preliminary and may be replaced by a more general
288
     * implementation of READ rights in future.<BR>
289
     * The default value is <code>true</code>.
290
     */
291
    @Override
292
    public boolean isPublish() {
293
        return publish;
294
    }
295

    
296
    @Override
297
    public void setPublish(boolean publish) {
298
        this.publish = publish;
299
    }
300

    
301
    /**
302
     * Returns the {@link eu.etaxonomy.cdm.model.reference.Reference reference} of <i>this</i> (abstract) taxon.
303
     * This is the reference or the treatment using the {@link TaxonName taxon name}
304
     * in <i>this</i> (abstract) taxon.
305
     */
306
    public Reference getSec() {
307
        return sec;
308
    }
309
    /**
310
     * @see  #getSec()
311
     */
312
    public void setSec(Reference sec) {
313
        this.sec = sec;
314
    }
315

    
316
    /**
317
     * @return the micro reference (detail) for the sec(undum)
318
     * reference
319
     * @see #getSec()
320
     */
321
    public String getSecMicroReference() {
322
        return secMicroReference;
323
    }
324

    
325
    /**
326
     * @see #getSecMicroReference()
327
     * @see #getSec()
328
     */
329
    public void setSecMicroReference(String secMicroReference) {
330
        this.secMicroReference = CdmUtils.Nb(secMicroReference);
331
    }
332

    
333

    
334

    
335
    /**
336
     * An appended phrase is a phrase that is added to the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name}
337
     * 's title cache to be used just in this taxon. E.g. the phrase "sensu latu" may be added
338
     * to the name to describe this taxon more precisely.
339
     * If {@link #isUseNameCache()}
340
     * @return the appendedPhrase
341
     */
342
    public String getAppendedPhrase() {
343
        return appendedPhrase;
344
    }
345

    
346
    /**
347
     * @param appendedPhrase the appendedPhrase to set
348
     */
349
    public void setAppendedPhrase(String appendedPhrase) {
350
        this.appendedPhrase = appendedPhrase;
351
    }
352

    
353
    /**
354
     * @return the useNameCache
355
     */
356
    public boolean isUseNameCache() {
357
        return useNameCache;
358
    }
359

    
360
    /**
361
     * @param useNameCache the useNameCache to set
362
     */
363
    public void setUseNameCache(boolean useNameCache) {
364
        this.useNameCache = useNameCache;
365
    }
366

    
367
    /**
368
     * Returns <code>true</code> if <code>this</code>
369
     * taxon base is not part of any classification.
370
     * False otherwise
371
     * @return boolean
372
     */
373
    @Transient
374
    public abstract boolean isOrphaned();
375

    
376

    
377
    /**
378
     * @return
379
     */
380
    @Transient
381
    public Rank getNullSafeRank() {
382
        return name == null ? null : name.getRank();
383
    }
384

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

    
409
        int result = 0;
410

    
411
        if (otherTaxon == null) {
412
            throw new NullPointerException("Cannot compare to null.");
413
        }
414

    
415
        otherTaxon = deproxy(otherTaxon);
416

    
417
        TaxonName otherName = deproxy(otherTaxon.getName());
418
        ITaxonNameBase thisName = this.getName();
419
        if ((otherName == null || thisName == null)){
420
            if (otherName != thisName){
421
                result = thisName == null ? -1 : 1;
422
            }
423
        }else{
424
            result = thisName.compareToName(otherName);
425
        }
426

    
427
        if (result == 0){
428
            String otherReferenceTitleCache = "";
429
            String thisReferenceTitleCache = "";
430

    
431
            Reference otherRef = deproxy(otherTaxon.getSec());
432
            if (otherRef != null) {
433
                otherReferenceTitleCache = otherRef.getTitleCache();
434
            }
435
            Reference thisRef = deproxy(this.getSec());
436
            if (thisRef != null) {
437
                thisReferenceTitleCache = thisRef.getTitleCache();
438
            }
439
            if ((CdmUtils.isNotBlank(otherReferenceTitleCache) || CdmUtils.isNotBlank(thisReferenceTitleCache))) {
440
                result = thisReferenceTitleCache.compareTo(otherReferenceTitleCache);
441
            }
442
        }
443

    
444
        return result;
445
    }
446

    
447
//*********************** CLONE ********************************************************/
448

    
449
    /**
450
     * Clones <i>this</i> taxon. This is a shortcut that enables to create
451
     * a new instance with exactly the same taxon name and sec reference.
452
     *
453
     * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
454
     * @see java.lang.Object#clone()
455
     */
456
    @Override
457
    public Object clone() {
458
        TaxonBase<?> result;
459
        try {
460
            result = (TaxonBase<?>)super.clone();
461
//            result.setSec(null);
462

    
463
            return result;
464
        } catch (CloneNotSupportedException e) {
465
            logger.warn("Object does not implement cloneable");
466
            e.printStackTrace();
467
            return null;
468
        }
469

    
470

    
471
    }
472

    
473

    
474
}
(10-10/21)