Project

General

Profile

Download (17.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.common;
11

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

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

    
40
import org.apache.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.hibernate.search.DefinedTermBaseClassBridge;
51
import eu.etaxonomy.cdm.model.ICdmUuidCacher;
52
import eu.etaxonomy.cdm.model.description.Feature;
53
import eu.etaxonomy.cdm.model.description.MeasurementUnit;
54
import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
55
import eu.etaxonomy.cdm.model.description.TextFormat;
56
import eu.etaxonomy.cdm.model.location.NamedAreaType;
57
import eu.etaxonomy.cdm.model.location.ReferenceSystem;
58
import eu.etaxonomy.cdm.model.media.Media;
59
import eu.etaxonomy.cdm.model.media.RightsType;
60
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
61
import eu.etaxonomy.cdm.model.occurrence.DerivationEventType;
62
import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;
63

    
64

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

    
107
    private static final long serialVersionUID = 2931811562248571531L;
108
    private static final Logger logger = Logger.getLogger(DefinedTermBase.class);
109

    
110
//	@XmlElement(name = "KindOf")
111
//    @XmlIDREF
112
//    @XmlSchemaType(name = "IDREF")
113
    @XmlTransient
114
    @ManyToOne(fetch = FetchType.LAZY, targetEntity = DefinedTermBase.class)
115
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
116
    private T kindOf;
117
    /**
118
     * FIXME - Hibernate returns this as a collection of CGLibProxy$$DefinedTermBase objects
119
     * which can't be cast to instances of T - can we explicitly initialize these terms using
120
     * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?
121
     */
122
//	@XmlElementWrapper(name = "Generalizations")
123
//	@XmlElement(name = "GeneralizationOf")
124
//    @XmlIDREF
125
//    @XmlSchemaType(name = "IDREF")
126
    @XmlTransient
127
    @OneToMany(fetch=FetchType.LAZY, mappedBy = "kindOf", targetEntity = DefinedTermBase.class)
128
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
129
    private Set<T> generalizationOf = new HashSet<>();
130

    
131
//	@XmlElement(name = "PartOf")
132
//	@XmlIDREF
133
//  @XmlSchemaType(name = "IDREF")
134
    @XmlTransient
135
    @ManyToOne(fetch = FetchType.LAZY, targetEntity = DefinedTermBase.class)
136
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
137
    protected T partOf;
138

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

    
153
    @XmlElementWrapper(name = "Media")
154
    @XmlElement(name = "Medium")
155
    @XmlIDREF
156
    @XmlSchemaType(name = "IDREF")
157
    @ManyToMany(fetch = FetchType.LAZY)
158
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
159
    private Set<Media> media = new HashSet<>();
160

    
161
    @XmlElement(name = "TermVocabulary")
162
    @XmlIDREF
163
    @XmlSchemaType(name = "IDREF")
164
    @ManyToOne(fetch=FetchType.LAZY)
165
//    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})  remove cascading #5754
166
    protected TermVocabulary<T> vocabulary;
167

    
168
    //the unique identifier/name this term uses in its given vocabulary #3479
169
    @XmlElement(name = "idInVocabulary")
170
    @Column(length=255)
171
    //TODO Val #3379, #4245
172
//  @NullOrNotEmpty
173
    private String idInVocabulary;  //the unique identifier/name this term uses in its given vocabulary #3479
174

    
175
    @XmlElement(name = "symbol")
176
    @Column(length=30)
177
    //the symbol to be used in String representations for this term  #5734
178
    //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
179
    //empty string is explicitly allowed and should be distinguished from NULL!
180
    private String symbol;
181

    
182
    @XmlElement(name = "symbol2")
183
    @Column(length=30)
184
    //the second symbol to be used in String representations for this term #7096
185
    //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
186
    //empty string is explicitly allowed and should be distinguished from NULL!
187
    private String symbol2;
188

    
189
//***************************** CONSTRUCTOR *******************************************/
190

    
191
    //for javassit only
192
    @Deprecated
193
    protected DefinedTermBase(){};
194

    
195
    protected DefinedTermBase(TermType type) {
196
        super(type);
197
    }
198
    public DefinedTermBase(TermType type, String description, String label, String labelAbbrev) {
199
        super(type, description, label, labelAbbrev);
200
    }
201

    
202

    
203
//********************** GETTER /SETTER *************************************
204

    
205
      @Override
206
      public String getIdInVocabulary() {
207
          return idInVocabulary;
208
      }
209

    
210
      @Override
211
      public void setIdInVocabulary(String idInVocabulary) {
212

    
213
          this.idInVocabulary = CdmUtils.isBlank(idInVocabulary)? null : idInVocabulary;
214
      }
215

    
216
      @Override
217
      public T getKindOf(){
218

    
219
          if (this instanceof HibernateProxy) {
220
              HibernateProxy proxy = (HibernateProxy) this;
221
              LazyInitializer li = proxy.getHibernateLazyInitializer();
222
              return (T)((T)li.getImplementation()).getKindOf();
223
          } else {
224
              return (T)DefinedTermBase.deproxy(this.kindOf, this.getClass());
225
          }
226
      }
227

    
228
      public void setKindOf(T kindOf){
229
          this.kindOf = kindOf;
230
      }
231

    
232

    
233
      @Override
234
      public Set<T> getGeneralizationOf(){
235
          return this.generalizationOf;
236
      }
237

    
238
      protected void setGeneralizationOf(Set<T> value) {
239
          this.generalizationOf = value;
240
      }
241

    
242
      public void addGeneralizationOf(T generalization) {
243
          checkTermType(generalization);
244
          generalization.setKindOf(this);
245
          this.generalizationOf.add(generalization);
246
      }
247

    
248

    
249
      public void removeGeneralization(T generalization) {
250
          if(generalizationOf.contains(generalization)){
251
              generalization.setKindOf(null);
252
              this.generalizationOf.remove(generalization);
253
          }
254
      }
255

    
256
      @Override
257
      public T getPartOf(){
258
          if (this instanceof HibernateProxy) {
259
              HibernateProxy proxy = (HibernateProxy) this;
260
              LazyInitializer li = proxy.getHibernateLazyInitializer();
261
              return (T)((T)li.getImplementation()).getPartOf();
262
          } else {
263
              return (T)DefinedTermBase.deproxy(this.partOf, this.getClass());
264
          }
265
      }
266

    
267
      /**
268
       * @see #getPartOf()
269
      */
270
      public void setPartOf(T partOf){
271
          this.partOf = partOf;
272
      }
273

    
274

    
275
    //TODO Comparable implemented only for fixing failing JAXB imports, may be removed when this is fixed
276
  	@Override
277
  	@Deprecated //for inner use only
278
  	public int compareTo(T other) {
279
		return ((Integer)this.getId()).compareTo(other.getId());
280
	}
281

    
282
	@Override
283
      public Set<T> getIncludes(){
284
          return this.includes;
285
      }
286

    
287
      /**
288
       * @see #getIncludes()
289
      */
290
      protected void setIncludes(Set<T> includes) {
291
          this.includes = includes;
292
      }
293

    
294
      /**
295
       * @see #getIncludes()
296
       */
297
      public void addIncludes(T includes) {
298
          checkTermType(includes);
299
          includes.setPartOf(this);
300
          this.includes.add(includes);
301
      }
302

    
303
      /**
304
       * @see #getIncludes()
305
       */
306
      public void removeIncludes(T includes) {
307
          if(this.includes.contains(includes)) {
308
              includes.setPartOf(null);
309
              this.includes.remove(includes);
310
          }
311
      }
312

    
313
      @Override
314
      public Set<Media> getMedia(){
315
          return this.media;
316
      }
317

    
318
      public void addMedia(Media media) {
319
          this.media.add(media);
320
      }
321
      public void removeMedia(Media media) {
322
          this.media.remove(media);
323
      }
324

    
325
      /**
326
       * @return
327
       */
328
      public TermVocabulary<T> getVocabulary() {
329
          return this.vocabulary;
330
      }
331

    
332
      //for bedirectional use only, use vocabulary.addTerm instead
333
      /**
334
       * @param newVocabulary
335
       */
336
      protected void setVocabulary(TermVocabulary<T> newVocabulary) {
337
          this.vocabulary = newVocabulary;
338
    }
339

    
340

    
341
    public String getSymbol() {
342
        return symbol;
343
    }
344
    public void setSymbol(String symbol) {
345
        this.symbol = symbol;
346
    }
347
    /**
348
     * @return the symbol2
349
     */
350
    public String getSymbol2() {
351
        return symbol2;
352
    }
353

    
354
    /**
355
     * @param symbol2 the symbol2 to set
356
     */
357
    public void setSymbol2(String symbol2) {
358
        this.symbol2 = symbol2;
359
    }
360

    
361
//******************************* METHODS ******************************************************/
362

    
363

    
364

    
365
    @Override
366
      public boolean isKindOf(T ancestor) {
367
          if (kindOf == null || ancestor == null){
368
            return false;
369
        }else if (kindOf.equals(ancestor)){
370
            return true;
371
        }else{
372
            return kindOf.isKindOf(ancestor);
373
        }
374
      }
375

    
376
      @Override
377
      public Set<T> getGeneralizationOf(boolean recursive) {
378
          Set<T> result = new HashSet<T>();
379
        result.addAll(this.generalizationOf);
380
        if (recursive){
381
            for (T child : this.generalizationOf){
382
                result.addAll(child.getGeneralizationOf());
383
            }
384
        }
385
        return result;
386
      }
387

    
388

    
389

    
390
    public abstract void resetTerms();
391

    
392
    protected abstract void setDefaultTerms(TermVocabulary<T> termVocabulary);
393

    
394

    
395
    @Override
396
    public T readCsvLine(Class<T> termClass, List<String> csvLine, TermType termType, Map<UUID,DefinedTermBase> terms, boolean abbrevAsId) {
397
        try {
398
            T newInstance = getInstance(termClass, termType);
399
            readCsvLine(newInstance, csvLine, Language.CSV_LANGUAGE(), abbrevAsId);
400
            readIsPartOf(newInstance, csvLine, terms);
401
            return newInstance;
402
        } catch (Exception e) {
403
            logger.error(e);
404
            for(StackTraceElement ste : e.getStackTrace()) {
405
                logger.error(ste);
406
            }
407
            throw new RuntimeException(e);
408
        }
409
    }
410

    
411
    protected static <TERM extends DefinedTermBase> TERM readCsvLine(TERM newInstance, List<String> csvLine, Language lang, boolean abbrevAsId) {
412
        newInstance.setUuid(UUID.fromString(csvLine.get(0)));
413
        String uriStr = CdmUtils.Ne(csvLine.get(1));
414
        newInstance.setUri(uriStr == null? null: URI.create(uriStr));
415
        String label = csvLine.get(2).trim();
416
        String description = CdmUtils.Ne(csvLine.get(3).trim());
417
        String abbreviatedLabel = CdmUtils.Ne(csvLine.get(4).trim());
418
        if (CdmUtils.isBlank(abbreviatedLabel)){
419
            abbreviatedLabel = null;
420
        }
421
        if (abbrevAsId){
422
            newInstance.setIdInVocabulary(abbreviatedLabel);  //new in 3.3
423
        }
424
        newInstance.addRepresentation(Representation.NewInstance(description, label, abbreviatedLabel, lang) );
425

    
426
        return newInstance;
427
    }
428

    
429
    protected void readIsPartOf(T newInstance, List<String> csvLine, Map<UUID, DefinedTermBase> terms){
430
        int index = partOfCsvLineIndex();
431
         if (index != -1){
432
            String partOfString = csvLine.get(index);
433
             if(CdmUtils.isNotBlank(partOfString)) {
434
                 UUID partOfUuid = UUID.fromString(partOfString);
435
                 DefinedTermBase partOf = terms.get(partOfUuid);
436
                 partOf.addIncludes(newInstance);
437
             }
438
         }
439

    
440
    }
441

    
442
    /**
443
     * Get the
444
     * @return
445
     */
446
    protected int partOfCsvLineIndex() {
447
        return -1;
448
    }
449

    
450

    
451
    private  <T extends DefinedTermBase> T getInstance(Class<? extends DefinedTermBase> termClass, TermType termType) {
452
        try {
453
            Constructor<T> c = ((Class<T>)termClass).getDeclaredConstructor();
454
            c.setAccessible(true);
455
            T termInstance = c.newInstance();
456
            termInstance.setTermType(termType);
457
            return termInstance;
458
        } catch (Exception e) {
459
            throw new RuntimeException(e);
460
        }
461
    }
462

    
463
    @Override
464
    public void writeCsvLine(CSVWriter writer, T term) {
465
        String [] line = new String[4];
466
        line[0] = term.getUuid().toString();
467
        line[1] = term.getUri().toString();
468
        line[2] = term.getLabel();
469
        line[3] = term.getDescription();
470
        writer.writeNext(line);
471
    }
472

    
473
    @Transient
474
    public T getByUuid(UUID uuid){
475
        return this.vocabulary.findTermByUuid(uuid);
476
    }
477

    
478
    /**
479
     * Throws {@link IllegalArgumentException} if the given
480
     * term has not the same term type as this term or if term type is null.
481
     * @param term
482
     */
483
    private void checkTermType(IHasTermType term) {
484
        IHasTermType.checkTermTypes(term, this);
485
    }
486

    
487

    
488
//*********************** CLONE ********************************************************/
489

    
490
    /**
491
     * Clones <i>this</i> DefinedTermBase. This is a shortcut that enables to create
492
     * a new instance that differs only slightly from <i>this</i> defined term base by
493
     * modifying only some of the attributes.
494
     *
495
     * @see eu.etaxonomy.cdm.model.common.TermBase#clone()
496
     * @see java.lang.Object#clone()
497
     */
498
    @Override
499
    public Object clone() {
500
        DefinedTermBase result;
501
        try {
502
            result = (DefinedTermBase) super.clone();
503
        }catch (CloneNotSupportedException e) {
504
            logger.warn("Object does not implement cloneable");
505
            e.printStackTrace();
506
            return null;
507
        }
508

    
509
        result.generalizationOf = new HashSet<DefinedTermBase>();
510
        for (DefinedTermBase generalizationOf : this.generalizationOf){
511
            result.generalizationOf.add(generalizationOf.clone());
512
        }
513

    
514
        result.includes = new HashSet<DefinedTermBase>();
515

    
516
        for (DefinedTermBase include: this.includes){
517
            result.includes.add(include.clone());
518
        }
519

    
520
        result.media = new HashSet<Media>();
521

    
522
        for (Media media: this.media){
523
            result.addMedia(media);
524
        }
525

    
526
        return result;
527
    }
528

    
529
    // Currently the CDM Caching mechanism is only used for caching terms
530
    private static ICdmUuidCacher cacher;
531

    
532

    
533
    /**
534
     * Gets the CDM cacher object
535
     *
536
     * @return the CDM cacher object
537
     */
538
    public static ICdmUuidCacher getCacher() {
539
		return cacher;
540
	}
541

    
542
	/**
543
	 * Sets the CDM cacher object
544
	 *
545
	 * @param cacher the CDM cacher object
546
	 */
547
	public static void setCacher(ICdmUuidCacher cacher) {
548
		DefinedTermBase.cacher = cacher;
549
	}
550

    
551
	public static <T extends DefinedTermBase> T getTermByClassAndUUID(Class<T> clazz, UUID uuid) {
552
	    if(cacher != null) {
553
	        Object obj = getCacher().load(uuid);
554
	        if(obj != null && obj.getClass().equals(clazz)) {
555
	            return (T)obj;
556
	        }
557
	    }
558
	    return null;
559
	}
560
}
(9-9/83)