Project

General

Profile

Download (15.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.Entity;
21
import javax.persistence.FetchType;
22
import javax.persistence.Inheritance;
23
import javax.persistence.InheritanceType;
24
import javax.persistence.ManyToMany;
25
import javax.persistence.ManyToOne;
26
import javax.persistence.OneToMany;
27
import javax.persistence.Transient;
28
import javax.xml.bind.annotation.XmlAccessType;
29
import javax.xml.bind.annotation.XmlAccessorType;
30
import javax.xml.bind.annotation.XmlElement;
31
import javax.xml.bind.annotation.XmlElementWrapper;
32
import javax.xml.bind.annotation.XmlIDREF;
33
import javax.xml.bind.annotation.XmlRootElement;
34
import javax.xml.bind.annotation.XmlSchemaType;
35
import javax.xml.bind.annotation.XmlSeeAlso;
36
import javax.xml.bind.annotation.XmlTransient;
37
import javax.xml.bind.annotation.XmlType;
38

    
39
import org.apache.log4j.Logger;
40
import org.codehaus.plexus.util.StringUtils;
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
import org.hibernate.validator.constraints.Length;
48

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

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

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

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

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

    
156
    @XmlElement(name = "TermVocabulary")
157
    @XmlIDREF
158
    @XmlSchemaType(name = "IDREF")
159
    @ManyToOne(fetch=FetchType.LAZY)
160
    @Cascade(CascadeType.SAVE_UPDATE)
161
    protected TermVocabulary<T> vocabulary;
162

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

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

    
172

    
173

    
174
//***************************** CONSTRUCTOR *******************************************/
175

    
176

    
177

    
178
	//for javassit only
179
    @Deprecated
180
    protected DefinedTermBase(){};
181

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

    
189

    
190
//********************** GETTER /SETTER *************************************
191

    
192
      @Override
193
      public String getIdInVocabulary() {
194
          return idInVocabulary;
195
      }
196

    
197
      @Override
198
      public void setIdInVocabulary(String idInVocabulary) {
199

    
200
          this.idInVocabulary = StringUtils.isBlank(idInVocabulary)? null : idInVocabulary;
201
      }
202

    
203
      @Override
204
      public T getKindOf(){
205

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

    
215
      public void setKindOf(T kindOf){
216
          this.kindOf = kindOf;
217
      }
218

    
219

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

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

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

    
234

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

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

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

    
260

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

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

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

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

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

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

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

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

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

    
325
//******************************* METHODS ******************************************************/
326

    
327

    
328
      @Override
329
      public boolean isKindOf(T ancestor) {
330
          if (kindOf == null || ancestor == null){
331
            return false;
332
        }else if (kindOf.equals(ancestor)){
333
            return true;
334
        }else{
335
            return kindOf.isKindOf(ancestor);
336
        }
337
      }
338

    
339
      @Override
340
      public Set<T> getGeneralizationOf(boolean recursive) {
341
          Set<T> result = new HashSet<T>();
342
        result.addAll(this.generalizationOf);
343
        if (recursive){
344
            for (T child : this.generalizationOf){
345
                result.addAll(child.getGeneralizationOf());
346
            }
347
        }
348
        return result;
349
      }
350

    
351

    
352

    
353
    public abstract void resetTerms();
354

    
355
    protected abstract void setDefaultTerms(TermVocabulary<T> termVocabulary);
356

    
357

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

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

    
388
        return newInstance;
389
    }
390

    
391
    protected void readIsPartOf(T newInstance, List<String> csvLine, Map<UUID, DefinedTermBase> terms){
392
        int index = partOfCsvLineIndex();
393
         if (index != -1){
394
            String partOfString = csvLine.get(index);
395
             if(StringUtils.isNotBlank(partOfString)) {
396
                 UUID partOfUuid = UUID.fromString(partOfString);
397
                 DefinedTermBase partOf = terms.get(partOfUuid);
398
                 partOf.addIncludes(newInstance);
399
             }
400
         }
401

    
402
    }
403

    
404
    /**
405
     * Get the
406
     * @return
407
     */
408
    protected int partOfCsvLineIndex() {
409
        return -1;
410
    }
411

    
412

    
413
    private  <T extends DefinedTermBase> T getInstance(Class<? extends DefinedTermBase> termClass) {
414
        try {
415
            Constructor<T> c = ((Class<T>)termClass).getDeclaredConstructor();
416
            c.setAccessible(true);
417
            T termInstance = c.newInstance();
418
            return termInstance;
419
        } catch (Exception e) {
420
            throw new RuntimeException(e);
421
        }
422
    }
423

    
424
    @Override
425
    public void writeCsvLine(CSVWriter writer, T term) {
426
        String [] line = new String[4];
427
        line[0] = term.getUuid().toString();
428
        line[1] = term.getUri().toString();
429
        line[2] = term.getLabel();
430
        line[3] = term.getDescription();
431
        writer.writeNext(line);
432
    }
433

    
434
    @Transient
435
    public T getByUuid(UUID uuid){
436
        return this.vocabulary.findTermByUuid(uuid);
437
    }
438

    
439

    
440
//*********************** CLONE ********************************************************/
441

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

    
461
        result.generalizationOf = new HashSet<DefinedTermBase>();
462
        for (DefinedTermBase generalizationOf : this.generalizationOf){
463
            result.generalizationOf.add(generalizationOf.clone());
464
        }
465

    
466
        result.includes = new HashSet<DefinedTermBase>();
467

    
468
        for (DefinedTermBase include: this.includes){
469
            result.includes.add(include.clone());
470
        }
471

    
472
        result.media = new HashSet<Media>();
473

    
474
        for (Media media: this.media){
475
            result.addMedia(media);
476
        }
477

    
478
        return result;
479
    }
480

    
481
    // Currently the CDM Caching mechanism is only used for caching terms
482
    private static ICdmCacher cacher;
483

    
484
    /**
485
     * Gets the CDM cacher object
486
     *
487
     * @return the CDM cacher object
488
     */
489
    public static ICdmCacher getCacher() {
490
		return cacher;
491
	}
492

    
493
	/**
494
	 * Sets the CDM cacher object
495
	 *
496
	 * @param cacher the CDM cacher object
497
	 */
498
	public static void setCacher(ICdmCacher cacher) {
499
		DefinedTermBase.cacher = cacher;
500
	}
501
}
(8-8/73)