Project

General

Profile

Download (17.6 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.log4j.Logger;
40
import org.hibernate.annotations.Cascade;
41
import org.hibernate.annotations.CascadeType;
42
import org.hibernate.envers.Audited;
43
import org.hibernate.proxy.HibernateProxy;
44
import org.hibernate.proxy.LazyInitializer;
45
import org.hibernate.search.annotations.ClassBridge;
46

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

    
69

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

    
113
    private static final long serialVersionUID = 2931811562248571531L;
114
    private static final Logger logger = Logger.getLogger(DefinedTermBase.class);
115

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

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

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

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

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

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

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

    
182
    @XmlElement(name = "symbol")
183
    @Column(length=30)
184
    //the symbol to be used in String representations for this term  #5734
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 symbol;
188

    
189
    @XmlElement(name = "symbol2")
190
    @Column(length=30)
191
    //the second symbol to be used in String representations for this term #7096
192
    //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
193
    //empty string is explicitly allowed and should be distinguished from NULL!
194
    private String symbol2;
195

    
196
    private ExternallyManaged externallyManaged;
197

    
198
//***************************** CONSTRUCTOR *******************************************/
199

    
200
    //for javassit only
201
    @Deprecated
202
    protected DefinedTermBase(){};
203

    
204
    protected DefinedTermBase(TermType type) {
205
        super(type);
206
    }
207
    public DefinedTermBase(TermType type, String description, String label, String labelAbbrev) {
208
        super(type, description, label, labelAbbrev);
209
    }
210

    
211

    
212
//********************** GETTER /SETTER *************************************
213

    
214
      @Override
215
      public String getIdInVocabulary() {
216
          return idInVocabulary;
217
      }
218

    
219
      @Override
220
      public void setIdInVocabulary(String idInVocabulary) {
221

    
222
          this.idInVocabulary = CdmUtils.isBlank(idInVocabulary)? null : idInVocabulary;
223
      }
224

    
225
      @Override
226
      public T getKindOf(){
227

    
228
          if (this instanceof HibernateProxy) {
229
              HibernateProxy proxy = (HibernateProxy) this;
230
              LazyInitializer li = proxy.getHibernateLazyInitializer();
231
              return (T)((T)li.getImplementation()).getKindOf();
232
          } else {
233
              return (T)DefinedTermBase.deproxy(this.kindOf, this.getClass());
234
          }
235
      }
236

    
237
      public void setKindOf(T kindOf){
238
          this.kindOf = kindOf;
239
      }
240

    
241

    
242
      @Override
243
      public Set<T> getGeneralizationOf(){
244
          return this.generalizationOf;
245
      }
246

    
247
      protected void setGeneralizationOf(Set<T> value) {
248
          this.generalizationOf = value;
249
      }
250

    
251
      public void addGeneralizationOf(T generalization) {
252
          checkTermType(generalization);
253
          generalization.setKindOf(this);
254
          this.generalizationOf.add(generalization);
255
      }
256

    
257

    
258
      public void removeGeneralization(T generalization) {
259
          if(generalizationOf.contains(generalization)){
260
              generalization.setKindOf(null);
261
              this.generalizationOf.remove(generalization);
262
          }
263
      }
264

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

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

    
283

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

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

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

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

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

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

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

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

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

    
343

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

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

    
358
//******************************* METHODS ******************************************************/
359

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

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

    
383
    public abstract void resetTerms();
384

    
385
    protected abstract void setDefaultTerms(TermVocabulary<T> termVocabulary);
386

    
387

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

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

    
419
        return newInstance;
420
    }
421

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

    
434
    protected int partOfCsvLineIndex() {
435
        return -1;
436
    }
437

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

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

    
460
    @Transient
461
    public T getByUuid(UUID uuid){
462
        return this.vocabulary.findTermByUuid(uuid);
463
    }
464

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

    
474

    
475
//*********************** CLONE ********************************************************/
476

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

    
490
            result.generalizationOf = new HashSet<>();
491
            for (DefinedTermBase<T> generalizationOf : this.generalizationOf){
492
                result.generalizationOf.add((T)generalizationOf.clone());
493
            }
494

    
495
            result.includes = new HashSet<>();
496

    
497
            for (DefinedTermBase<?> include: this.includes){
498
                result.includes.add((T)include.clone());
499
            }
500

    
501
            result.media = new HashSet<>();
502

    
503
            for (Media media: this.media){
504
                result.addMedia(media);
505
            }
506

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

    
515
    // Currently the CDM Caching mechanism is only used for caching terms
516
    private static ICdmUuidCacher cacher;
517

    
518

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

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

    
537
	public static <T extends DefinedTermBase> T getTermByClassAndUUID(Class<T> clazz, UUID uuid) {
538
	    if(cacher != null) {
539
	        Object obj = getCacher().load(uuid);
540
	        if(obj != null && obj.getClass().equals(clazz)) {
541
	            return (T)obj;
542
	        }
543
	    }
544
	    return null;
545
	}
546
}
(3-3/33)