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.codehaus.plexus.util.StringUtils;
42
import org.hibernate.annotations.Cascade;
43
import org.hibernate.annotations.CascadeType;
44
import org.hibernate.envers.Audited;
45
import org.hibernate.proxy.HibernateProxy;
46
import org.hibernate.proxy.LazyInitializer;
47
import org.hibernate.search.annotations.ClassBridge;
48

    
49
import au.com.bytecode.opencsv.CSVWriter;
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
 * @created 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> extends TermBase implements ILoadableTerm<T>, IDefinedTerm<T>, Comparable<T> {
103
    private static final long serialVersionUID = 2931811562248571531L;
104
    private static final Logger logger = Logger.getLogger(DefinedTermBase.class);
105

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

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

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

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

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

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

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

    
178
//***************************** CONSTRUCTOR *******************************************/
179

    
180
    //for javassit only
181
    @Deprecated
182
    protected DefinedTermBase(){};
183

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

    
191

    
192
//********************** GETTER /SETTER *************************************
193

    
194
      @Override
195
      public String getIdInVocabulary() {
196
          return idInVocabulary;
197
      }
198

    
199
      @Override
200
      public void setIdInVocabulary(String idInVocabulary) {
201

    
202
          this.idInVocabulary = StringUtils.isBlank(idInVocabulary)? null : idInVocabulary;
203
      }
204

    
205
      @Override
206
      public T getKindOf(){
207

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

    
217
      public void setKindOf(T kindOf){
218
          this.kindOf = kindOf;
219
      }
220

    
221

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

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

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

    
236

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

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

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

    
262

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

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

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

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

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

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

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

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

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

    
327

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

    
335
//******************************* METHODS ******************************************************/
336

    
337

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

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

    
361

    
362

    
363
    public abstract void resetTerms();
364

    
365
    protected abstract void setDefaultTerms(TermVocabulary<T> termVocabulary);
366

    
367

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

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

    
398
        return newInstance;
399
    }
400

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

    
412
    }
413

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

    
422

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

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

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

    
449

    
450
//*********************** CLONE ********************************************************/
451

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

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

    
476
        result.includes = new HashSet<DefinedTermBase>();
477

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

    
482
        result.media = new HashSet<Media>();
483

    
484
        for (Media media: this.media){
485
            result.addMedia(media);
486
        }
487

    
488
        return result;
489
    }
490

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

    
494

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

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

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