Project

General

Profile

Download (17.4 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 ILoadableTerm<T>, IDefinedTerm<T>, Comparable<T> {
106
    private static final long serialVersionUID = 2931811562248571531L;
107
    private static final Logger logger = Logger.getLogger(DefinedTermBase.class);
108

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

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

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

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

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

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

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

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

    
188
//***************************** CONSTRUCTOR *******************************************/
189

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

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

    
201

    
202
//********************** GETTER /SETTER *************************************
203

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

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

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

    
215
      @Override
216
      public T getKindOf(){
217

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

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

    
231

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

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

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

    
247

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

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

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

    
273

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

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

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

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

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

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

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

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

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

    
339

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

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

    
360
//******************************* METHODS ******************************************************/
361

    
362

    
363

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

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

    
387

    
388

    
389
    public abstract void resetTerms();
390

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

    
393

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

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

    
425
        return newInstance;
426
    }
427

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

    
439
    }
440

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

    
449

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

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

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

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

    
485

    
486
//*********************** CLONE ********************************************************/
487

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

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

    
512
        result.includes = new HashSet<DefinedTermBase>();
513

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

    
518
        result.media = new HashSet<Media>();
519

    
520
        for (Media media: this.media){
521
            result.addMedia(media);
522
        }
523

    
524
        return result;
525
    }
526

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

    
530

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

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

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