Project

General

Profile

Download (18.1 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.term;
11

    
12
import java.lang.reflect.Constructor;
13
import java.util.HashSet;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.Set;
17
import java.util.UUID;
18

    
19
import javax.persistence.Column;
20
import javax.persistence.Entity;
21
import javax.persistence.FetchType;
22
import javax.persistence.Inheritance;
23
import javax.persistence.InheritanceType;
24
import javax.persistence.ManyToMany;
25
import javax.persistence.ManyToOne;
26
import javax.persistence.OneToMany;
27
import javax.persistence.Transient;
28
import javax.xml.bind.annotation.XmlAccessType;
29
import javax.xml.bind.annotation.XmlAccessorType;
30
import javax.xml.bind.annotation.XmlElement;
31
import javax.xml.bind.annotation.XmlElementWrapper;
32
import javax.xml.bind.annotation.XmlIDREF;
33
import javax.xml.bind.annotation.XmlRootElement;
34
import javax.xml.bind.annotation.XmlSchemaType;
35
import javax.xml.bind.annotation.XmlSeeAlso;
36
import javax.xml.bind.annotation.XmlTransient;
37
import javax.xml.bind.annotation.XmlType;
38

    
39
import org.apache.logging.log4j.LogManager;
40
import org.apache.logging.log4j.Logger;
41
import org.hibernate.annotations.Cascade;
42
import org.hibernate.annotations.CascadeType;
43
import org.hibernate.envers.Audited;
44
import org.hibernate.proxy.HibernateProxy;
45
import org.hibernate.proxy.LazyInitializer;
46
import org.hibernate.search.annotations.ClassBridge;
47

    
48
import au.com.bytecode.opencsv.CSVWriter;
49
import eu.etaxonomy.cdm.common.CdmUtils;
50
import eu.etaxonomy.cdm.common.URI;
51
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
52
import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseClassBridge;
53
import eu.etaxonomy.cdm.model.ICdmUuidCacher;
54
import eu.etaxonomy.cdm.model.common.AnnotationType;
55
import eu.etaxonomy.cdm.model.common.ExtensionType;
56
import eu.etaxonomy.cdm.model.common.ExternallyManaged;
57
import eu.etaxonomy.cdm.model.common.Language;
58
import eu.etaxonomy.cdm.model.common.MarkerType;
59
import eu.etaxonomy.cdm.model.description.Feature;
60
import eu.etaxonomy.cdm.model.description.MeasurementUnit;
61
import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
62
import eu.etaxonomy.cdm.model.description.TextFormat;
63
import eu.etaxonomy.cdm.model.location.NamedAreaType;
64
import eu.etaxonomy.cdm.model.location.ReferenceSystem;
65
import eu.etaxonomy.cdm.model.media.Media;
66
import eu.etaxonomy.cdm.model.media.RightsType;
67
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
68
import eu.etaxonomy.cdm.model.occurrence.DerivationEventType;
69
import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;
70

    
71

    
72
/**
73
 * workaround for enumerations, base type according to TDWG.  For linear ordering
74
 * use partOf relation and BreadthFirst. Default iterator order should therefore
75
 * be BreadthFirst (not DepthFirst)
76
 * @author m.doering
77
 * @since 08-Nov-2007 13:06:19
78
 */
79
@XmlAccessorType(XmlAccessType.FIELD)
80
@XmlType(name = "DefinedTermBase", propOrder = {
81
    "media",
82
    "vocabulary",
83
    "idInVocabulary",
84
    "symbol",
85
    "symbol2",
86
    "externallyManaged",
87
})
88
@XmlRootElement(name = "DefinedTermBase")
89
@XmlSeeAlso({
90
    AnnotationType.class,
91
    DerivationEventType.class,
92
    DefinedTerm.class,
93
    ExtensionType.class,
94
    Feature.class,
95
    Language.class,
96
    MarkerType.class,
97
    MeasurementUnit.class,
98
    NamedAreaType.class,
99
    NomenclaturalCode.class,
100
    PreservationMethod.class,
101
    ReferenceSystem.class,
102
    RightsType.class,
103
    StatisticalMeasure.class,
104
    TextFormat.class
105
})
106
@Entity
107
@Audited
108
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
109
@ClassBridge(impl=DefinedTermBaseClassBridge.class)
110
//TODO Comparable implemented only for fixing failing JAXB import, may be removed when this is fixed
111
public abstract class DefinedTermBase<T extends DefinedTermBase>
112
            extends TermBase
113
            implements IDefinedTerm<T>, Comparable<T> {
114

    
115
    private static final long serialVersionUID = 2931811562248571531L;
116
    private static final Logger logger = LogManager.getLogger(DefinedTermBase.class);
117

    
118
//	@XmlElement(name = "KindOf")
119
//    @XmlIDREF
120
//    @XmlSchemaType(name = "IDREF")
121
    @XmlTransient
122
    @ManyToOne(fetch = FetchType.LAZY, targetEntity = DefinedTermBase.class)
123
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
124
    private T kindOf;
125

    
126
    /**
127
     * FIXME - Hibernate returns this as a collection of CGLibProxy$$DefinedTermBase objects
128
     * which can't be cast to instances of T - can we explicitly initialize these terms using
129
     * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?
130
     */
131
//	@XmlElementWrapper(name = "Generalizations")
132
//	@XmlElement(name = "GeneralizationOf")
133
//    @XmlIDREF
134
//    @XmlSchemaType(name = "IDREF")
135
    @XmlTransient
136
    @OneToMany(fetch=FetchType.LAZY, mappedBy = "kindOf", targetEntity = DefinedTermBase.class)
137
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
138
    private Set<T> generalizationOf = new HashSet<>();
139

    
140
//	@XmlElement(name = "PartOf")
141
//	@XmlIDREF
142
//  @XmlSchemaType(name = "IDREF")
143
    @XmlTransient
144
    @ManyToOne(fetch = FetchType.LAZY, targetEntity = DefinedTermBase.class)
145
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
146
    protected T partOf;
147

    
148
    /**
149
     * FIXME - Hibernate retuns this as a collection of CGLibProxy$$DefinedTermBase objects
150
     * which can't be cast to instances of T - can we explicitly initialize these terms using
151
     * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?
152
     */
153
//	@XmlElementWrapper(name = "Includes")
154
//	@XmlElement(name = "Include")
155
//	@XmlIDREF
156
//    @XmlSchemaType(name = "IDREF")
157
    @XmlTransient
158
    @OneToMany(fetch=FetchType.LAZY, mappedBy = "partOf", targetEntity = DefinedTermBase.class)
159
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
160
    private Set<T> includes = new HashSet<>();
161

    
162
    @XmlElementWrapper(name = "Media")
163
    @XmlElement(name = "Medium")
164
    @XmlIDREF
165
    @XmlSchemaType(name = "IDREF")
166
    @ManyToMany(fetch = FetchType.LAZY)
167
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
168
    private Set<Media> media = new HashSet<>();
169

    
170
    @XmlElement(name = "TermVocabulary")
171
    @XmlIDREF
172
    @XmlSchemaType(name = "IDREF")
173
    @ManyToOne(fetch=FetchType.LAZY)
174
//    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})  remove cascading #5754
175
    protected TermVocabulary<T> vocabulary;
176

    
177
    //the unique identifier/name this term uses in its given vocabulary #3479
178
    @XmlElement(name = "idInVocabulary")
179
    @Column(length=255)
180
    //TODO Val #3379, #4245
181
//  @NullOrNotEmpty
182
    private String idInVocabulary;  //the unique identifier/name this term uses in its given vocabulary #3479
183

    
184
    @XmlElement(name = "symbol")
185
    @Column(length=30)
186
    //the symbol to be used in String representations for this term  #5734
187
    //this term can be changed by the database instance even if the term is not managed by this instance as it is only for representation and has no semantic or identifying character
188
    //empty string is explicitly allowed and should be distinguished from NULL!
189
    private String symbol;
190

    
191
    @XmlElement(name = "symbol2")
192
    @Column(length=30)
193
    //the second symbol to be used in String representations for this term #7096
194
    //this term can be changed by the database instance even if the term is not managed by this instance as it is only for representation and has no semantic or identifying character
195
    //empty string is explicitly allowed and should be distinguished from NULL!
196
    private String symbol2;
197

    
198
    private ExternallyManaged externallyManaged;
199

    
200
//***************************** CONSTRUCTOR *******************************************/
201

    
202
    //for hibernate use only, *packet* private required by bytebuddy
203
    //2022-06-17: currently still needed protected as TaxEditor.TaxonRelationshipTypeInverseContainer inherits from DefinedTermBase
204
    @Deprecated
205
    protected DefinedTermBase(){}
206

    
207
    protected DefinedTermBase(TermType type) {
208
        super(type);
209
    }
210

    
211
    public DefinedTermBase(TermType type, String description, String label, String labelAbbrev, Language lang) {
212
        super(type, description, label, labelAbbrev, lang);
213
    }
214
    public DefinedTermBase(TermType type, String description, String label, String labelAbbrev) {
215
        super(type, description, label, labelAbbrev, null);
216
    }
217

    
218
//********************** GETTER /SETTER *************************************
219

    
220
      @Override
221
      public String getIdInVocabulary() {
222
          return idInVocabulary;
223
      }
224

    
225
      @Override
226
      public void setIdInVocabulary(String idInVocabulary) {
227
          this.idInVocabulary = CdmUtils.isBlank(idInVocabulary)? null : idInVocabulary;
228
      }
229

    
230
      @Override
231
      public T getKindOf(){
232
          if (this instanceof HibernateProxy) {
233
              HibernateProxy proxy = (HibernateProxy) this;
234
              LazyInitializer li = proxy.getHibernateLazyInitializer();
235
              return (T)((T)li.getImplementation()).getKindOf();
236
          } else {
237
              return (T)DefinedTermBase.deproxy(this.kindOf, this.getClass());
238
          }
239
      }
240

    
241
      public void setKindOf(T kindOf){
242
          this.kindOf = kindOf;
243
      }
244

    
245
      @Override
246
      public Set<T> getGeneralizationOf(){
247
          return this.generalizationOf;
248
      }
249
      protected void setGeneralizationOf(Set<T> value) {
250
          this.generalizationOf = value;
251
      }
252
      public void addGeneralizationOf(T generalization) {
253
          checkTermType(generalization);
254
          generalization.setKindOf(this);
255
          this.generalizationOf.add(generalization);
256
      }
257
      public void removeGeneralization(T generalization) {
258
          if(generalizationOf.contains(generalization)){
259
              generalization.setKindOf(null);
260
              this.generalizationOf.remove(generalization);
261
          }
262
      }
263

    
264
      @Override
265
      public T getPartOf(){
266
          if (this instanceof HibernateProxy) {
267
              HibernateProxy proxy = (HibernateProxy) this;
268
              LazyInitializer li = proxy.getHibernateLazyInitializer();
269
              return (T)((T)li.getImplementation()).getPartOf();
270
          } else {
271
              return (T)DefinedTermBase.deproxy(this.partOf, this.getClass());
272
          }
273
      }
274

    
275
      /**
276
       * @see #getPartOf()
277
      */
278
      public void setPartOf(T partOf){
279
          this.partOf = partOf;
280
      }
281

    
282

    
283
    //TODO Comparable implemented only for fixing failing JAXB imports, may be removed when this is fixed
284
  	@Override
285
  	@Deprecated //for inner use only
286
  	public int compareTo(T other) {
287
		return ((Integer)this.getId()).compareTo(other.getId());
288
	}
289

    
290
	@Override
291
      public Set<T> getIncludes(){
292
          return this.includes;
293
      }
294

    
295
      /**
296
       * @see #getIncludes()
297
      */
298
      protected void setIncludes(Set<T> includes) {
299
          this.includes = includes;
300
      }
301

    
302
      /**
303
       * @see #getIncludes()
304
       */
305
      public void addIncludes(T includes) {
306
          checkTermType(includes);
307
          includes.setPartOf(this);
308
          this.includes.add(includes);
309
      }
310

    
311
      /**
312
       * @see #getIncludes()
313
       */
314
      public void removeIncludes(T includes) {
315
          if(this.includes.contains(includes)) {
316
              includes.setPartOf(null);
317
              this.includes.remove(includes);
318
          }
319
      }
320

    
321
      @Override
322
      public Set<Media> getMedia(){
323
          return this.media;
324
      }
325

    
326
      public void addMedia(Media media) {
327
          this.media.add(media);
328
      }
329
      public void removeMedia(Media media) {
330
          this.media.remove(media);
331
      }
332

    
333
      public TermVocabulary<T> getVocabulary() {
334
          return this.vocabulary;
335
      }
336

    
337
      //for bedirectional use only, use vocabulary.addTerm instead
338
      protected void setVocabulary(TermVocabulary<T> newVocabulary) {
339
          this.vocabulary = newVocabulary;
340
    }
341

    
342
    public String getSymbol() {
343
        return symbol;
344
    }
345
    public void setSymbol(String symbol) {
346
        this.symbol = symbol;
347
    }
348

    
349
    public String getSymbol2() {
350
        return symbol2;
351
    }
352
    public void setSymbol2(String symbol2) {
353
        this.symbol2 = symbol2;
354
    }
355

    
356
//******************************* METHODS ******************************************************/
357

    
358
    @Override
359
      public boolean isKindOf(T ancestor) {
360
          if (kindOf == null || ancestor == null){
361
              return false;
362
          }else if (kindOf.equals(ancestor)){
363
              return true;
364
          }else{
365
              return kindOf.isKindOf(ancestor);
366
          }
367
      }
368

    
369
      @Override
370
      public Set<T> getGeneralizationOf(boolean recursive) {
371
          Set<T> result = new HashSet<T>();
372
        result.addAll(this.generalizationOf);
373
        if (recursive){
374
            for (T child : this.generalizationOf){
375
                result.addAll(child.getGeneralizationOf());
376
            }
377
        }
378
        return result;
379
      }
380

    
381
    public abstract void resetTerms();
382

    
383
    protected abstract void setDefaultTerms(TermVocabulary<T> termVocabulary);
384

    
385
    @Override
386
    public T readCsvLine(Class<T> termClass, List<String> csvLine, TermType termType, Map<UUID,DefinedTermBase> terms, boolean abbrevAsId) {
387
        try {
388
            T newInstance = getInstance(termClass, termType);
389
            readCsvLine(newInstance, csvLine, Language.CSV_LANGUAGE(), abbrevAsId);
390
            readIsPartOf(newInstance, csvLine, terms);
391
            return newInstance;
392
        } catch (Exception e) {
393
            logger.error(e);
394
            for(StackTraceElement ste : e.getStackTrace()) {
395
                logger.error(ste);
396
            }
397
            throw new RuntimeException(e);
398
        }
399
    }
400

    
401
    protected static <TERM extends DefinedTermBase> TERM readCsvLine(TERM newInstance, List<String> csvLine, Language lang, boolean abbrevAsId) {
402
        newInstance.setUuid(UUID.fromString(csvLine.get(0)));
403
        String uriStr = CdmUtils.Ne(csvLine.get(1));
404
        newInstance.setUri(uriStr == null? null: URI.create(uriStr));
405
        String label = csvLine.get(2).trim();
406
        String description = CdmUtils.Ne(csvLine.get(3).trim());
407
        String abbreviatedLabel = CdmUtils.Ne(csvLine.get(4).trim());
408
        if (CdmUtils.isBlank(abbreviatedLabel)){
409
            abbreviatedLabel = null;
410
        }
411
        if (abbrevAsId){
412
            newInstance.setIdInVocabulary(abbreviatedLabel);  //new in 3.3
413
        }
414
        newInstance.addRepresentation(Representation.NewInstance(description, label, abbreviatedLabel, lang) );
415

    
416
        return newInstance;
417
    }
418

    
419
    protected void readIsPartOf(T newInstance, List<String> csvLine, Map<UUID, DefinedTermBase> terms){
420
        int index = partOfCsvLineIndex();
421
         if (index != -1){
422
            String partOfString = csvLine.get(index);
423
             if(CdmUtils.isNotBlank(partOfString)) {
424
                 UUID partOfUuid = UUID.fromString(partOfString);
425
                 DefinedTermBase partOf = terms.get(partOfUuid);
426
                 partOf.addIncludes(newInstance);
427
             }
428
         }
429
    }
430

    
431
    protected int partOfCsvLineIndex() {
432
        return -1;
433
    }
434

    
435
    private  <T extends DefinedTermBase> T getInstance(Class<? extends DefinedTermBase> termClass, TermType termType) {
436
        try {
437
            Constructor<T> c = ((Class<T>)termClass).getDeclaredConstructor();
438
            c.setAccessible(true);
439
            T termInstance = c.newInstance();
440
            termInstance.setTermType(termType);
441
            return termInstance;
442
        } catch (Exception e) {
443
            throw new RuntimeException(e);
444
        }
445
    }
446

    
447
    @Override
448
    public void writeCsvLine(CSVWriter writer, T term) {
449
        String [] line = new String[4];
450
        line[0] = term.getUuid().toString();
451
        line[1] = term.getUri().toString();
452
        line[2] = term.getLabel();
453
        line[3] = term.getDescription();
454
        writer.writeNext(line);
455
    }
456

    
457
    @Transient
458
    public T getByUuid(UUID uuid){
459
        return this.vocabulary.findTermByUuid(uuid);
460
    }
461

    
462
    /**
463
     * Throws {@link IllegalArgumentException} if the given
464
     * term has not the same term type as this term or if term type is null.
465
     * @param term
466
     */
467
    private void checkTermType(IHasTermType term) {
468
        IHasTermType.checkTermTypes(term, this);
469
    }
470

    
471

    
472
//*********************** CLONE ********************************************************/
473

    
474
    /**
475
     * Clones <i>this</i> DefinedTermBase. This is a shortcut that enables to create
476
     * a new instance that differs only slightly from <i>this</i> defined term base by
477
     * modifying only some of the attributes.
478
     *
479
     * @see eu.etaxonomy.cdm.model.term.TermBase#clone()
480
     * @see java.lang.Object#clone()
481
     */
482
    @Override
483
    public DefinedTermBase<T> clone() {
484
        try {
485
            DefinedTermBase<T> result = (DefinedTermBase<T>) super.clone();
486

    
487
            result.generalizationOf = new HashSet<>();
488
            for (DefinedTermBase<T> generalizationOf : this.generalizationOf){
489
                result.generalizationOf.add((T)generalizationOf.clone());
490
            }
491

    
492
            result.includes = new HashSet<>();
493

    
494
            for (DefinedTermBase<?> include: this.includes){
495
                result.includes.add((T)include.clone());
496
            }
497

    
498
            result.media = new HashSet<>();
499

    
500
            for (Media media: this.media){
501
                result.addMedia(media);
502
            }
503

    
504
            return result;
505
        }catch (CloneNotSupportedException e) {
506
            logger.warn("Object does not implement cloneable");
507
            e.printStackTrace();
508
            return null;
509
        }
510
    }
511

    
512
    // Currently the CDM Caching mechanism is only used for caching terms
513
    private static ICdmUuidCacher cacher;
514

    
515

    
516
    /**
517
     * Gets the CDM cacher object
518
     *
519
     * @return the CDM cacher object
520
     */
521
    public static ICdmUuidCacher getCacher() {
522
		return cacher;
523
	}
524

    
525
	/**
526
	 * Sets the CDM cacher object
527
	 *
528
	 * @param cacher the CDM cacher object
529
	 */
530
	public static void setCacher(ICdmUuidCacher cacher) {
531
		DefinedTermBase.cacher = cacher;
532
	}
533

    
534
	public static <T extends DefinedTermBase> T getTermByClassAndUUID(Class<T> clazz, UUID uuid) {
535
	    if(cacher != null) {
536
	        Object obj = HibernateProxyHelper.deproxy(getCacher().load(uuid));
537

    
538
	        if(obj != null && obj.getClass().equals(clazz)) {
539
	            return (T)obj;
540
	        }
541
	    }
542
	    return null;
543
	}
544
}
(5-5/32)