Project

General

Profile

Download (16.2 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})
162
    protected TermVocabulary<T> vocabulary;
163

    
164
  //the unique iedentifier/name this term uses in its given vocabulary #3479
165
   //open issues: is null allowed? If not, implement unique constraint
166

    
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=10)
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

    
183

    
184
	//for javassit only
185
    @Deprecated
186
    protected DefinedTermBase(){};
187

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

    
195

    
196
//********************** GETTER /SETTER *************************************
197

    
198
      @Override
199
      public String getIdInVocabulary() {
200
          return idInVocabulary;
201
      }
202

    
203
      @Override
204
      public void setIdInVocabulary(String idInVocabulary) {
205

    
206
          this.idInVocabulary = StringUtils.isBlank(idInVocabulary)? null : idInVocabulary;
207
      }
208

    
209
      @Override
210
      public T getKindOf(){
211

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

    
221
      public void setKindOf(T kindOf){
222
          this.kindOf = kindOf;
223
      }
224

    
225

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

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

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

    
240

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

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

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

    
266

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

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

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

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

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

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

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

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

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

    
331
//******************************* METHODS ******************************************************/
332

    
333

    
334
      @Override
335
      public boolean isKindOf(T ancestor) {
336
          if (kindOf == null || ancestor == null){
337
            return false;
338
        }else if (kindOf.equals(ancestor)){
339
            return true;
340
        }else{
341
            return kindOf.isKindOf(ancestor);
342
        }
343
      }
344

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

    
357

    
358

    
359
    public abstract void resetTerms();
360

    
361
    protected abstract void setDefaultTerms(TermVocabulary<T> termVocabulary);
362

    
363

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

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

    
394
        return newInstance;
395
    }
396

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

    
408
    }
409

    
410
    /**
411
     * Get the
412
     * @return
413
     */
414
    protected int partOfCsvLineIndex() {
415
        return -1;
416
    }
417

    
418

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

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

    
440
    @Transient
441
    public T getByUuid(UUID uuid){
442
        return this.vocabulary.findTermByUuid(uuid);
443
    }
444

    
445

    
446
//*********************** CLONE ********************************************************/
447

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

    
467
        result.generalizationOf = new HashSet<DefinedTermBase>();
468
        for (DefinedTermBase generalizationOf : this.generalizationOf){
469
            result.generalizationOf.add(generalizationOf.clone());
470
        }
471

    
472
        result.includes = new HashSet<DefinedTermBase>();
473

    
474
        for (DefinedTermBase include: this.includes){
475
            result.includes.add(include.clone());
476
        }
477

    
478
        result.media = new HashSet<Media>();
479

    
480
        for (Media media: this.media){
481
            result.addMedia(media);
482
        }
483

    
484
        return result;
485
    }
486

    
487
    // Currently the CDM Caching mechanism is only used for caching terms
488
    private static ICdmUuidCacher cacher;
489

    
490

    
491
    /**
492
     * Gets the CDM cacher object
493
     *
494
     * @return the CDM cacher object
495
     */
496
    public static ICdmUuidCacher getCacher() {
497
		return cacher;
498
	}
499

    
500
	/**
501
	 * Sets the CDM cacher object
502
	 *
503
	 * @param cacher the CDM cacher object
504
	 */
505
	public static void setCacher(ICdmUuidCacher cacher) {
506
		DefinedTermBase.cacher = cacher;
507
	}
508

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