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

    
187
//***************************** CONSTRUCTOR *******************************************/
188

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

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

    
200

    
201
//********************** GETTER /SETTER *************************************
202

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

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

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

    
214
      @Override
215
      public T getKindOf(){
216

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

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

    
230

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

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

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

    
245

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

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

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

    
271

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

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

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

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

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

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

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

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

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

    
336

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

    
344
//******************************* METHODS ******************************************************/
345

    
346

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

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

    
370

    
371

    
372
    public abstract void resetTerms();
373

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

    
376

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

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

    
407
        return newInstance;
408
    }
409

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

    
421
    }
422

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

    
431

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

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

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

    
458

    
459
//*********************** CLONE ********************************************************/
460

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

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

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

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

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

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

    
497
        return result;
498
    }
499

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

    
503

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

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

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