Project

General

Profile

Download (16.3 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
})
79
@XmlRootElement(name = "DefinedTermBase")
80
@XmlSeeAlso({
81
    AnnotationType.class,
82
    DerivationEventType.class,
83
    DefinedTerm.class,
84
    ExtensionType.class,
85
    Feature.class,
86
    Language.class,
87
    MarkerType.class,
88
    MeasurementUnit.class,
89
    NamedAreaType.class,
90
    NomenclaturalCode.class,
91
    PreservationMethod.class,
92
    ReferenceSystem.class,
93
    RightsType.class,
94
    StatisticalMeasure.class,
95
    TextFormat.class
96
})
97
@Entity
98
@Audited
99
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
100
@ClassBridge(impl=DefinedTermBaseClassBridge.class)
101
//TODO Comparable implemented only for fixing failing JAXB import, may be removed when this is fixed
102
public abstract class DefinedTermBase<T extends DefinedTermBase>
103
            extends TermBase
104
            implements ILoadableTerm<T>, IDefinedTerm<T>, Comparable<T> {
105
    private static final long serialVersionUID = 2931811562248571531L;
106
    private static final Logger logger = Logger.getLogger(DefinedTermBase.class);
107

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

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

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

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

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

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

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

    
180
//***************************** CONSTRUCTOR *******************************************/
181

    
182
    //for javassit only
183
    @Deprecated
184
    protected DefinedTermBase(){};
185

    
186
    protected DefinedTermBase(TermType type) {
187
        super(type);
188
    }
189
    public DefinedTermBase(TermType type, String description, String label, String labelAbbrev) {
190
        super(type, description, label, labelAbbrev);
191
    }
192

    
193

    
194
//********************** GETTER /SETTER *************************************
195

    
196
      @Override
197
      public String getIdInVocabulary() {
198
          return idInVocabulary;
199
      }
200

    
201
      @Override
202
      public void setIdInVocabulary(String idInVocabulary) {
203

    
204
          this.idInVocabulary = CdmUtils.isBlank(idInVocabulary)? null : idInVocabulary;
205
      }
206

    
207
      @Override
208
      public T getKindOf(){
209

    
210
          if (this instanceof HibernateProxy) {
211
              HibernateProxy proxy = (HibernateProxy) this;
212
              LazyInitializer li = proxy.getHibernateLazyInitializer();
213
              return (T)((T)li.getImplementation()).getKindOf();
214
          } else {
215
              return (T)DefinedTermBase.deproxy(this.kindOf, this.getClass());
216
          }
217
      }
218

    
219
      public void setKindOf(T kindOf){
220
          this.kindOf = kindOf;
221
      }
222

    
223

    
224
      @Override
225
      public Set<T> getGeneralizationOf(){
226
          return this.generalizationOf;
227
      }
228

    
229
      protected void setGeneralizationOf(Set<T> value) {
230
          this.generalizationOf = value;
231
      }
232

    
233
      public void addGeneralizationOf(T generalization) {
234
          generalization.setKindOf(this);
235
          this.generalizationOf.add(generalization);
236
      }
237

    
238

    
239
      public void removeGeneralization(T generalization) {
240
          if(generalizationOf.contains(generalization)){
241
              generalization.setKindOf(null);
242
              this.generalizationOf.remove(generalization);
243
          }
244
      }
245

    
246
      @Override
247
      public T getPartOf(){
248
          if (this instanceof HibernateProxy) {
249
              HibernateProxy proxy = (HibernateProxy) this;
250
              LazyInitializer li = proxy.getHibernateLazyInitializer();
251
              return (T)((T)li.getImplementation()).getPartOf();
252
          } else {
253
              return (T)DefinedTermBase.deproxy(this.partOf, this.getClass());
254
          }
255
      }
256

    
257
      /**
258
       * @see #getPartOf()
259
      */
260
      public void setPartOf(T partOf){
261
          this.partOf = partOf;
262
      }
263

    
264

    
265
    //TODO Comparable implemented only for fixing failing JAXB imports, may be removed when this is fixed
266
  	@Override
267
  	@Deprecated //for inner use only
268
  	public int compareTo(T other) {
269
		return ((Integer)this.getId()).compareTo(other.getId());
270
	}
271

    
272
	@Override
273
      public Set<T> getIncludes(){
274
          return this.includes;
275
      }
276

    
277
      /**
278
       * @see #getIncludes()
279
      */
280
      protected void setIncludes(Set<T> includes) {
281
          this.includes = includes;
282
      }
283

    
284
      /**
285
       * @see #getIncludes()
286
       */
287
      public void addIncludes(T includes) {
288
          includes.setPartOf(this);
289
          this.includes.add(includes);
290
      }
291

    
292
      /**
293
       * @see #getIncludes()
294
       */
295
      public void removeIncludes(T includes) {
296
          if(this.includes.contains(includes)) {
297
              includes.setPartOf(null);
298
              this.includes.remove(includes);
299
          }
300
      }
301

    
302
      @Override
303
      public Set<Media> getMedia(){
304
          return this.media;
305
      }
306

    
307
      public void addMedia(Media media) {
308
          this.media.add(media);
309
      }
310
      public void removeMedia(Media media) {
311
          this.media.remove(media);
312
      }
313

    
314
      /**
315
       * @return
316
       */
317
      public TermVocabulary<T> getVocabulary() {
318
          return this.vocabulary;
319
      }
320

    
321
      //for bedirectional use only, use vocabulary.addTerm instead
322
      /**
323
       * @param newVocabulary
324
       */
325
      protected void setVocabulary(TermVocabulary<T> newVocabulary) {
326
          this.vocabulary = newVocabulary;
327
    }
328

    
329

    
330
    public String getSymbol() {
331
        return symbol;
332
    }
333
    public void setSymbol(String symbol) {
334
        this.symbol = symbol;
335
    }
336

    
337
//******************************* METHODS ******************************************************/
338

    
339

    
340
      @Override
341
      public boolean isKindOf(T ancestor) {
342
          if (kindOf == null || ancestor == null){
343
            return false;
344
        }else if (kindOf.equals(ancestor)){
345
            return true;
346
        }else{
347
            return kindOf.isKindOf(ancestor);
348
        }
349
      }
350

    
351
      @Override
352
      public Set<T> getGeneralizationOf(boolean recursive) {
353
          Set<T> result = new HashSet<T>();
354
        result.addAll(this.generalizationOf);
355
        if (recursive){
356
            for (T child : this.generalizationOf){
357
                result.addAll(child.getGeneralizationOf());
358
            }
359
        }
360
        return result;
361
      }
362

    
363

    
364

    
365
    public abstract void resetTerms();
366

    
367
    protected abstract void setDefaultTerms(TermVocabulary<T> termVocabulary);
368

    
369

    
370
    @Override
371
    public T readCsvLine(Class<T> termClass, List<String> csvLine, Map<UUID,DefinedTermBase> terms, boolean abbrevAsId) {
372
        try {
373
            T newInstance = getInstance(termClass);
374
            readCsvLine(newInstance, csvLine, Language.CSV_LANGUAGE(), abbrevAsId);
375
            readIsPartOf(newInstance, csvLine, terms);
376
            return newInstance;
377
        } catch (Exception e) {
378
            logger.error(e);
379
            for(StackTraceElement ste : e.getStackTrace()) {
380
                logger.error(ste);
381
            }
382
            throw new RuntimeException(e);
383
        }
384
    }
385

    
386
    protected static <TERM extends DefinedTermBase> TERM readCsvLine(TERM newInstance, List<String> csvLine, Language lang, boolean abbrevAsId) {
387
        newInstance.setUuid(UUID.fromString(csvLine.get(0)));
388
        newInstance.setUri( URI.create(csvLine.get(1)));
389
        String label = csvLine.get(2).trim();
390
        String description = csvLine.get(3);
391
        String abbreviatedLabel = csvLine.get(4);
392
        if (CdmUtils.isBlank(abbreviatedLabel)){
393
            abbreviatedLabel = null;
394
        }
395
        if (abbrevAsId){
396
            newInstance.setIdInVocabulary(abbreviatedLabel);  //new in 3.3
397
        }
398
        newInstance.addRepresentation(Representation.NewInstance(description, label, abbreviatedLabel, lang) );
399

    
400
        return newInstance;
401
    }
402

    
403
    protected void readIsPartOf(T newInstance, List<String> csvLine, Map<UUID, DefinedTermBase> terms){
404
        int index = partOfCsvLineIndex();
405
         if (index != -1){
406
            String partOfString = csvLine.get(index);
407
             if(CdmUtils.isNotBlank(partOfString)) {
408
                 UUID partOfUuid = UUID.fromString(partOfString);
409
                 DefinedTermBase partOf = terms.get(partOfUuid);
410
                 partOf.addIncludes(newInstance);
411
             }
412
         }
413

    
414
    }
415

    
416
    /**
417
     * Get the
418
     * @return
419
     */
420
    protected int partOfCsvLineIndex() {
421
        return -1;
422
    }
423

    
424

    
425
    private  <T extends DefinedTermBase> T getInstance(Class<? extends DefinedTermBase> termClass) {
426
        try {
427
            Constructor<T> c = ((Class<T>)termClass).getDeclaredConstructor();
428
            c.setAccessible(true);
429
            T termInstance = c.newInstance();
430
            return termInstance;
431
        } catch (Exception e) {
432
            throw new RuntimeException(e);
433
        }
434
    }
435

    
436
    @Override
437
    public void writeCsvLine(CSVWriter writer, T term) {
438
        String [] line = new String[4];
439
        line[0] = term.getUuid().toString();
440
        line[1] = term.getUri().toString();
441
        line[2] = term.getLabel();
442
        line[3] = term.getDescription();
443
        writer.writeNext(line);
444
    }
445

    
446
    @Transient
447
    public T getByUuid(UUID uuid){
448
        return this.vocabulary.findTermByUuid(uuid);
449
    }
450

    
451

    
452
//*********************** CLONE ********************************************************/
453

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

    
473
        result.generalizationOf = new HashSet<DefinedTermBase>();
474
        for (DefinedTermBase generalizationOf : this.generalizationOf){
475
            result.generalizationOf.add(generalizationOf.clone());
476
        }
477

    
478
        result.includes = new HashSet<DefinedTermBase>();
479

    
480
        for (DefinedTermBase include: this.includes){
481
            result.includes.add(include.clone());
482
        }
483

    
484
        result.media = new HashSet<Media>();
485

    
486
        for (Media media: this.media){
487
            result.addMedia(media);
488
        }
489

    
490
        return result;
491
    }
492

    
493
    // Currently the CDM Caching mechanism is only used for caching terms
494
    private static ICdmUuidCacher cacher;
495

    
496

    
497
    /**
498
     * Gets the CDM cacher object
499
     *
500
     * @return the CDM cacher object
501
     */
502
    public static ICdmUuidCacher getCacher() {
503
		return cacher;
504
	}
505

    
506
	/**
507
	 * Sets the CDM cacher object
508
	 *
509
	 * @param cacher the CDM cacher object
510
	 */
511
	public static void setCacher(ICdmUuidCacher cacher) {
512
		DefinedTermBase.cacher = cacher;
513
	}
514

    
515
	public static <T extends DefinedTermBase> T getTermByClassAndUUID(Class<T> clazz, UUID uuid) {
516
	    if(cacher != null) {
517
	        Object obj = getCacher().load(uuid);
518
	        if(obj != null && obj.getClass().equals(clazz)) {
519
	            return (T)obj;
520
	        }
521
	    }
522
	    return null;
523
	}
524
}
(9-9/79)