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 IDefinedTerm<T>, Comparable<T> {
106

    
107
    private static final long serialVersionUID = 2931811562248571531L;
108
    private static final Logger logger = Logger.getLogger(DefinedTermBase.class);
109

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

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

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

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

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

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

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

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

    
189
//***************************** CONSTRUCTOR *******************************************/
190

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

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

    
202

    
203
//********************** GETTER /SETTER *************************************
204

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

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

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

    
216
      @Override
217
      public T getKindOf(){
218

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

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

    
232

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

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

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

    
248

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

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

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

    
274

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

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

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

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

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

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

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

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

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

    
340

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

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

    
361
//******************************* METHODS ******************************************************/
362

    
363

    
364

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

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

    
388

    
389

    
390
    public abstract void resetTerms();
391

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

    
394

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

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

    
426
        return newInstance;
427
    }
428

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

    
440
    }
441

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

    
450

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

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

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

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

    
486

    
487
//*********************** CLONE ********************************************************/
488

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

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

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

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

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

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

    
525
        return result;
526
    }
527

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

    
531

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

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

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