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.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
          generalization.setKindOf(this);
243
          this.generalizationOf.add(generalization);
244
      }
245

    
246

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

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

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

    
272

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

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

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

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

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

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

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

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

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

    
337

    
338
    public String getSymbol() {
339
        return symbol;
340
    }
341
    public void setSymbol(String symbol) {
342
        this.symbol = symbol;
343
    }
344

    
345
//******************************* METHODS ******************************************************/
346

    
347

    
348
      @Override
349
      public boolean isKindOf(T ancestor) {
350
          if (kindOf == null || ancestor == null){
351
            return false;
352
        }else if (kindOf.equals(ancestor)){
353
            return true;
354
        }else{
355
            return kindOf.isKindOf(ancestor);
356
        }
357
      }
358

    
359
      @Override
360
      public Set<T> getGeneralizationOf(boolean recursive) {
361
          Set<T> result = new HashSet<T>();
362
        result.addAll(this.generalizationOf);
363
        if (recursive){
364
            for (T child : this.generalizationOf){
365
                result.addAll(child.getGeneralizationOf());
366
            }
367
        }
368
        return result;
369
      }
370

    
371

    
372

    
373
    public abstract void resetTerms();
374

    
375
    protected abstract void setDefaultTerms(TermVocabulary<T> termVocabulary);
376

    
377

    
378
    @Override
379
    public T readCsvLine(Class<T> termClass, List<String> csvLine, Map<UUID,DefinedTermBase> terms, boolean abbrevAsId) {
380
        try {
381
            T newInstance = getInstance(termClass);
382
            readCsvLine(newInstance, csvLine, Language.CSV_LANGUAGE(), abbrevAsId);
383
            readIsPartOf(newInstance, csvLine, terms);
384
            return newInstance;
385
        } catch (Exception e) {
386
            logger.error(e);
387
            for(StackTraceElement ste : e.getStackTrace()) {
388
                logger.error(ste);
389
            }
390
            throw new RuntimeException(e);
391
        }
392
    }
393

    
394
    protected static <TERM extends DefinedTermBase> TERM readCsvLine(TERM newInstance, List<String> csvLine, Language lang, boolean abbrevAsId) {
395
        newInstance.setUuid(UUID.fromString(csvLine.get(0)));
396
        newInstance.setUri( URI.create(csvLine.get(1)));
397
        String label = csvLine.get(2).trim();
398
        String description = CdmUtils.Ne(csvLine.get(3).trim());
399
        String abbreviatedLabel = CdmUtils.Ne(csvLine.get(4).trim());
400
        if (CdmUtils.isBlank(abbreviatedLabel)){
401
            abbreviatedLabel = null;
402
        }
403
        if (abbrevAsId){
404
            newInstance.setIdInVocabulary(abbreviatedLabel);  //new in 3.3
405
        }
406
        newInstance.addRepresentation(Representation.NewInstance(description, label, abbreviatedLabel, lang) );
407

    
408
        return newInstance;
409
    }
410

    
411
    protected void readIsPartOf(T newInstance, List<String> csvLine, Map<UUID, DefinedTermBase> terms){
412
        int index = partOfCsvLineIndex();
413
         if (index != -1){
414
            String partOfString = csvLine.get(index);
415
             if(CdmUtils.isNotBlank(partOfString)) {
416
                 UUID partOfUuid = UUID.fromString(partOfString);
417
                 DefinedTermBase partOf = terms.get(partOfUuid);
418
                 partOf.addIncludes(newInstance);
419
             }
420
         }
421

    
422
    }
423

    
424
    /**
425
     * Get the
426
     * @return
427
     */
428
    protected int partOfCsvLineIndex() {
429
        return -1;
430
    }
431

    
432

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

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

    
454
    @Transient
455
    public T getByUuid(UUID uuid){
456
        return this.vocabulary.findTermByUuid(uuid);
457
    }
458

    
459

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

    
462
    /**
463
     * Clones <i>this</i> DefinedTermBase. This is a shortcut that enables to create
464
     * a new instance that differs only slightly from <i>this</i> defined term base by
465
     * modifying only some of the attributes.
466
     *
467
     * @see eu.etaxonomy.cdm.model.common.TermBase#clone()
468
     * @see java.lang.Object#clone()
469
     */
470
    @Override
471
    public Object clone() {
472
        DefinedTermBase result;
473
        try {
474
            result = (DefinedTermBase) super.clone();
475
        }catch (CloneNotSupportedException e) {
476
            logger.warn("Object does not implement cloneable");
477
            e.printStackTrace();
478
            return null;
479
        }
480

    
481
        result.generalizationOf = new HashSet<DefinedTermBase>();
482
        for (DefinedTermBase generalizationOf : this.generalizationOf){
483
            result.generalizationOf.add(generalizationOf.clone());
484
        }
485

    
486
        result.includes = new HashSet<DefinedTermBase>();
487

    
488
        for (DefinedTermBase include: this.includes){
489
            result.includes.add(include.clone());
490
        }
491

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

    
494
        for (Media media: this.media){
495
            result.addMedia(media);
496
        }
497

    
498
        return result;
499
    }
500

    
501
    // Currently the CDM Caching mechanism is only used for caching terms
502
    private static ICdmUuidCacher cacher;
503

    
504

    
505
    /**
506
     * Gets the CDM cacher object
507
     *
508
     * @return the CDM cacher object
509
     */
510
    public static ICdmUuidCacher getCacher() {
511
		return cacher;
512
	}
513

    
514
	/**
515
	 * Sets the CDM cacher object
516
	 *
517
	 * @param cacher the CDM cacher object
518
	 */
519
	public static void setCacher(ICdmUuidCacher cacher) {
520
		DefinedTermBase.cacher = cacher;
521
	}
522

    
523
	public static <T extends DefinedTermBase> T getTermByClassAndUUID(Class<T> clazz, UUID uuid) {
524
	    if(cacher != null) {
525
	        Object obj = getCacher().load(uuid);
526
	        if(obj != null && obj.getClass().equals(clazz)) {
527
	            return (T)obj;
528
	        }
529
	    }
530
	    return null;
531
	}
532
}
(9-9/80)