Project

General

Profile

Download (16.7 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.Entity;
16
import javax.persistence.FetchType;
17
import javax.persistence.Index;
18
import javax.persistence.ManyToOne;
19
import javax.persistence.OneToOne;
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.compare.taxon.TaxonComparator;
46
import eu.etaxonomy.cdm.compare.taxon.TaxonNodeByNameComparator;
47
import eu.etaxonomy.cdm.compare.taxon.TaxonNodeByRankAndNameComparator;
48
import eu.etaxonomy.cdm.compare.taxon.TaxonNodeNaturalComparator;
49
import eu.etaxonomy.cdm.hibernate.search.AcceptedTaxonBridge;
50
import eu.etaxonomy.cdm.hibernate.search.ClassInfoBridge;
51
import eu.etaxonomy.cdm.model.common.IIntextReferenceTarget;
52
import eu.etaxonomy.cdm.model.common.IPublishable;
53
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
54
import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
55
import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
56
import eu.etaxonomy.cdm.model.name.ITaxonNameBase;
57
import eu.etaxonomy.cdm.model.name.Rank;
58
import eu.etaxonomy.cdm.model.name.TaxonName;
59
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
60
import eu.etaxonomy.cdm.model.reference.Reference;
61
import eu.etaxonomy.cdm.strategy.cache.TaggedText;
62
import eu.etaxonomy.cdm.strategy.cache.name.CacheUpdate;
63
import eu.etaxonomy.cdm.strategy.cache.taxon.ITaxonCacheStrategy;
64
import eu.etaxonomy.cdm.strategy.cache.taxon.TaxonBaseDefaultCacheStrategy;
65
import eu.etaxonomy.cdm.validation.Level2;
66
import eu.etaxonomy.cdm.validation.Level3;
67
import eu.etaxonomy.cdm.validation.annotation.TaxonNameCannotBeAcceptedAndSynonym;
68

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

    
115
    private static final long serialVersionUID = -3589185949928938529L;
116
    private static final Logger logger = Logger.getLogger(TaxonBase.class);
117

    
118
    public static final String ACC_TAXON_BRIDGE_PREFIX = "accTaxon";
119

    
120
    private static Method methodTaxonNameAddTaxonBase;
121

    
122
    static {
123
        try {
124
            methodTaxonNameAddTaxonBase = TaxonName.class.getDeclaredMethod("addTaxonBase", TaxonBase.class);
125
            methodTaxonNameAddTaxonBase.setAccessible(true);
126
        } catch (Exception e) {
127
            logger.error(e);
128
            for(StackTraceElement ste : e.getStackTrace()) {
129
                logger.error(ste);
130
            }
131
        }
132
    }
133

    
134
    //The assignment to the Taxon or to the Synonym class is not definitive
135
    @XmlAttribute(name = "isDoubtful")
136
    private boolean doubtful;
137

    
138
    @XmlElement(name = "Name")
139
    @XmlIDREF
140
    @XmlSchemaType(name = "IDREF")
141
    @ManyToOne(fetch = FetchType.LAZY)
142
    @IndexedEmbedded(includeEmbeddedObjectId=true)
143
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
144
    @NotNull(groups = Level2.class)
145
    private TaxonName name;
146

    
147
    //#9327
148
    @XmlElement(name = "SecSource")
149
    @XmlIDREF
150
    @XmlSchemaType(name = "IDREF")
151
    @OneToOne(fetch = FetchType.LAZY, orphanRemoval=true, mappedBy="sourcedTaxon")
152
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE,CascadeType.DELETE})
153
    @CacheUpdate(noUpdate ="titleCache")
154
    @IndexedEmbedded
155
    private SecundumSource secSource;
156

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

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

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

    
167
// ************* CONSTRUCTORS *************/
168

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

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

    
196
    @Override
197
    protected void initDefaultCacheStrategy() {
198
        this.cacheStrategy = (S)new TaxonBaseDefaultCacheStrategy();
199
    }
200

    
201
//********* METHODS **************************************/
202

    
203
    @Transient
204
    public List<TaggedText> getTaggedTitle(){
205
        return getCacheStrategy().getTaggedTitle(this);
206
    }
207

    
208
    /**
209
     * Returns the {@link TaxonName taxon name} used in <i>this</i> (abstract) taxon.
210
     */
211
    public TaxonName getName(){
212
        return this.name;
213
    }
214

    
215
    public void setName(TaxonName name) {
216
        if (this.name != null){
217
            this.name.getTaxonBases().remove(this);
218
        }
219
        if(name != null) {
220
            name.getTaxonBases().add(this);
221
        }
222
        this.name = name;
223
    }
224

    
225
    /**
226
     * Returns the {@link eu.etaxonomy.cdm.model.name.HomotypicalGroup homotypical group} of the
227
     * {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name} used in <i>this</i> (abstract) taxon.
228
     */
229
    @Transient
230
    public HomotypicalGroup getHomotypicGroup(){
231
        if (this.getName() == null){
232
            return null;
233
        }else{
234
            return this.getName().getHomotypicalGroup();
235
        }
236
    }
237

    
238
    /**
239
     * Returns the boolean value indicating whether the assignment of <i>this</i>
240
     * (abstract) taxon to the {@link Taxon Taxon} or to the {@link Synonym Synonym} class
241
     * is definitive (false) or not (true). If this flag is set the use of <i>this</i> (abstract)
242
     * taxon as an "accepted/correct" name or as a (junior) "synonym" might
243
     * still change in the course of taxonomical working process.
244
     */
245
    public boolean isDoubtful(){
246
        return this.doubtful;
247
    }
248
    /**
249
     * @see  #isDoubtful()
250
     */
251
    public void setDoubtful(boolean doubtful){
252
        this.doubtful = doubtful;
253
    }
254

    
255
    /**
256
     * Returns the boolean value indicating if this taxon should be withheld (<code>publish=false</code>) or not
257
     * (<code>publish=true</code>) during any publication process to the general public.
258
     * This publish flag implementation is preliminary and may be replaced by a more general
259
     * implementation of READ rights in future.<BR>
260
     * The default value is <code>true</code>.
261
     */
262
    @Override
263
    public boolean isPublish() {
264
        return publish;
265
    }
266
    @Override
267
    public void setPublish(boolean publish) {
268
        this.publish = publish;
269
    }
270

    
271
  //*************** sec source *******************/
272

    
273
    public SecundumSource getSecSource(){
274
        return this.secSource;
275
    }
276

    
277
    protected DescriptionElementSource getSecSource(boolean createIfNotExist){
278
        if (this.secSource == null && createIfNotExist){
279
            setSecSource(SecundumSource.NewSecundumInstance(this));
280
        }
281
        return secSource;
282
    }
283

    
284
    public void setSecSource(SecundumSource secSource) throws IllegalArgumentException {
285
        //check state
286
        if (secSource != null && !OriginalSourceType.SecundumReference.equals(secSource.getType())
287
                ){
288
            throw new IllegalArgumentException("Secundum source must be of type " + OriginalSourceType.SecundumReference.getLabel());
289
        }
290
        this.secSource = secSource;
291
        if (secSource != null && secSource.getSourcedTaxon() != this){
292
            secSource.setSourcedTaxon(this);
293
        }
294
    }
295

    
296
    @Transient
297
    public Reference getSec(){
298
        return this.secSource == null? null:this.secSource.getCitation();
299
    }
300

    
301
    @Transient
302
    public void setSec(Reference secReference){
303
        if (secReference == null && this.getSecSource()==null){
304
            return;
305
        }else{
306
            getSecSource(true).setCitation(secReference);
307
        }
308
    }
309

    
310
    /**
311
     * Returns the details string of the {@link #getSec() sec reference} assigned
312
     * to <i>this</i> taxon base. The details describe the exact localisation within
313
     * the publication used as sec reference. These are mostly
314
     * (implicitly) pages but can also be figures or tables or any other
315
     * element of a publication. A sec micro reference (details)
316
     * requires the existence of a sec reference.
317
     */
318
    @Transient
319
    public String getSecMicroReference(){
320
        return this.secSource == null? null: this.secSource.getCitationMicroReference();
321
    }
322

    
323
    /**
324
     * @see  #getSecMicroReference()
325
     */
326
    public void setSecMicroReference(String secMicroReference){
327
        secMicroReference = isBlank(secMicroReference)? null : secMicroReference;
328
        if (secMicroReference == null && this.getSecSource()==null){
329
            return;
330
        }else{
331
            this.getSecSource(true).setCitationMicroReference(secMicroReference);
332
        }
333
    }
334

    
335
    /**
336
     * Checks if the source is completely empty and if empty removes it from the name.
337
     */
338
    //TODO maybe this should be moved to a hibernate listener, but the listener solution may
339
    //work only for sec single sources as they are the only which are bidirectional
340
    protected void checkNullSource() {
341
        if (this.secSource != null && this.secSource.checkEmpty(true)){
342
            this.secSource = null;
343
        }
344
    }
345

    
346
// *********************************************
347

    
348
    /**
349
     * An appended phrase is a phrase that is added to the {@link eu.etaxonomy.cdm.model.name.TaxonName taxon name}
350
     * 's title cache to be used just in this taxon. E.g. the phrase "sensu latu" may be added
351
     * to the name to describe this taxon more precisely.
352
     * If {@link #isUseNameCache()}
353
     * @return the appendedPhrase
354
     */
355
    public String getAppendedPhrase() {
356
        return appendedPhrase;
357
    }
358

    
359
    /**
360
     * @param appendedPhrase the appendedPhrase to set
361
     */
362
    public void setAppendedPhrase(String appendedPhrase) {
363
        this.appendedPhrase = appendedPhrase;
364
    }
365

    
366
    /**
367
     * @return the useNameCache
368
     */
369
    public boolean isUseNameCache() {
370
        return useNameCache;
371
    }
372

    
373
    /**
374
     * @param useNameCache the useNameCache to set
375
     */
376
    public void setUseNameCache(boolean useNameCache) {
377
        this.useNameCache = useNameCache;
378
    }
379

    
380
    /**
381
     * Returns <code>true</code> if <code>this</code>
382
     * taxon base is not part of any classification.
383
     * False otherwise
384
     * @return boolean
385
     */
386
    @Transient
387
    public abstract boolean isOrphaned();
388

    
389

    
390
    /**
391
     * @return
392
     */
393
    @Transient
394
    public Rank getNullSafeRank() {
395
        return name == null ? null : name.getRank();
396
    }
397

    
398
    /**
399
     * This method compares 2 taxa on it's name titles and caches.
400
     * If both are equal it compares on the secundum titleCache as well.
401
     * It is not fully clear/defined how this method relates to
402
     * explicit comparators like {@link TaxonComparator}. The later
403
     * currently uses this method.
404
     * Historically it was a compareTo method in {@link IdentifiableEntity}
405
     * but did not fulfill the {@link Comparable} contract.
406
     * <BR><BR>
407
     * {@link  https://dev.e-taxonomy.eu/redmine/issues/922}<BR>
408
     * {@link https://dev.e-taxonomy.eu/redmine/issues/6311}
409
     *
410
     * @see ITaxonNameBase#compareToName(TaxonName)
411
     * @see TaxonComparator
412
     * @see TaxonNodeNaturalComparator
413
     * @see TaxonNodeByNameComparator
414
     * @see TaxonNodeByRankAndNameComparator
415
     * @param otherTaxon
416
     * @return the compareTo result similar to {@link Comparable#compareTo(Object)}
417
     * @throws NullPointerException if otherTaxon is <code>null</code>
418
     */
419
    //TODO handling of protected titleCache
420
    public int compareToTaxon(TaxonBase otherTaxon){
421

    
422
        int result = 0;
423

    
424
        if (otherTaxon == null) {
425
            throw new NullPointerException("Cannot compare to null.");
426
        }
427

    
428
        otherTaxon = deproxy(otherTaxon);
429

    
430
        TaxonName otherName = deproxy(otherTaxon.getName());
431
        ITaxonNameBase thisName = this.getName();
432
        if ((otherName == null || thisName == null)){
433
            if (otherName != thisName){
434
                result = thisName == null ? -1 : 1;
435
            }
436
        }else{
437
            result = thisName.compareToName(otherName);
438
        }
439

    
440
        if (result == 0){
441
            String otherReferenceTitleCache = "";
442
            String thisReferenceTitleCache = "";
443

    
444
            Reference otherRef = deproxy(otherTaxon.getSec());
445
            if (otherRef != null) {
446
                otherReferenceTitleCache = otherRef.getTitleCache();
447
            }
448
            Reference thisRef = deproxy(this.getSec());
449
            if (thisRef != null) {
450
                thisReferenceTitleCache = thisRef.getTitleCache();
451
            }
452
            if ((CdmUtils.isNotBlank(otherReferenceTitleCache) || CdmUtils.isNotBlank(thisReferenceTitleCache))) {
453
                result = thisReferenceTitleCache.compareTo(otherReferenceTitleCache);
454
            }
455
        }
456

    
457
        return result;
458
    }
459

    
460
//*********************** CLONE ********************************************************/
461

    
462
    /**
463
     * Clones <i>this</i> taxon. This is a shortcut that enables to create
464
     * a new instance with exactly the same taxon name and sec reference.
465
     *
466
     * @see java.lang.Object#clone()
467
     */
468
    @Override
469
    public TaxonBase<S> clone() {
470
        TaxonBase<S> result;
471
        try {
472
            result = (TaxonBase<S>)super.clone();
473

    
474
//            result.setSec(null);
475

    
476
            return result;
477
        } catch (CloneNotSupportedException e) {
478
            logger.warn("Object does not implement cloneable");
479
            e.printStackTrace();
480
            return null;
481
        }
482
    }
483
}
(9-9/16)