2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.model
.common
;
12 import java
.lang
.reflect
.Constructor
;
14 import java
.util
.HashSet
;
15 import java
.util
.List
;
18 import java
.util
.UUID
;
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
;
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
;
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
;
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)
70 * @created 08-Nov-2007 13:06:19
72 @XmlAccessorType(XmlAccessType
.FIELD
)
73 @XmlType(name
= "DefinedTermBase", propOrder
= {
78 @XmlRootElement(name
= "DefinedTermBase")
81 DerivationEventType
.class,
87 MeasurementUnit
.class,
89 NomenclaturalCode
.class,
90 PreservationMethod
.class,
91 ReferenceSystem
.class,
93 StatisticalMeasure
.class,
98 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
99 @ClassBridge(impl
=DefinedTermBaseClassBridge
.class)
100 public abstract class DefinedTermBase
<T
extends DefinedTermBase
> extends TermBase
implements ILoadableTerm
<T
>, IDefinedTerm
<T
> {
101 private static final long serialVersionUID
= 2931811562248571531L;
102 private static final Logger logger
= Logger
.getLogger(DefinedTermBase
.class);
104 // @XmlElement(name = "KindOf")
106 // @XmlSchemaType(name = "IDREF")
108 @ManyToOne(fetch
= FetchType
.LAZY
, targetEntity
= DefinedTermBase
.class)
109 @Cascade(CascadeType
.SAVE_UPDATE
)
112 * FIXME - Hibernate returns this as a collection of CGLibProxy$$DefinedTermBase objects
113 * which can't be cast to instances of T - can we explicitly initialize these terms using
114 * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?
116 // @XmlElementWrapper(name = "Generalizations")
117 // @XmlElement(name = "GeneralizationOf")
119 // @XmlSchemaType(name = "IDREF")
121 @OneToMany(fetch
=FetchType
.LAZY
, mappedBy
= "kindOf", targetEntity
= DefinedTermBase
.class)
122 @Cascade({CascadeType
.SAVE_UPDATE
})
123 private Set
<T
> generalizationOf
= new HashSet
<T
>();
125 // @XmlElement(name = "PartOf")
127 // @XmlSchemaType(name = "IDREF")
129 @ManyToOne(fetch
= FetchType
.LAZY
, targetEntity
= DefinedTermBase
.class)
130 @Cascade(CascadeType
.SAVE_UPDATE
)
134 * FIXME - Hibernate retuns this as a collection of CGLibProxy$$DefinedTermBase objects
135 * which can't be cast to instances of T - can we explicitly initialize these terms using
136 * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?
138 // @XmlElementWrapper(name = "Includes")
139 // @XmlElement(name = "Include")
141 // @XmlSchemaType(name = "IDREF")
143 @OneToMany(fetch
=FetchType
.LAZY
, mappedBy
= "partOf", targetEntity
= DefinedTermBase
.class)
144 @Cascade({CascadeType
.SAVE_UPDATE
})
145 private Set
<T
> includes
= new HashSet
<T
>();
147 @XmlElementWrapper(name
= "Media")
148 @XmlElement(name
= "Medium")
150 @XmlSchemaType(name
= "IDREF")
151 @ManyToMany(fetch
= FetchType
.LAZY
)
152 @Cascade({CascadeType
.SAVE_UPDATE
})
153 private Set
<Media
> media
= new HashSet
<Media
>();
155 @XmlElement(name
= "TermVocabulary")
157 @XmlSchemaType(name
= "IDREF")
158 @ManyToOne(fetch
=FetchType
.LAZY
)
159 @Cascade(CascadeType
.SAVE_UPDATE
)
160 protected TermVocabulary
<T
> vocabulary
;
162 //the unique iedentifier/name this term uses in its given vocabulary #3479
163 //open issues: is null allowed? If not, implement unique constraint
165 @XmlElement(name
= "idInVocabulary")
167 private String idInVocabulary
; //the unique identifier/name this term uses in its given vocabulary #3479
171 //***************************** CONSTRUCTOR *******************************************/
177 protected DefinedTermBase(){};
179 protected DefinedTermBase(TermType type
) {
182 public DefinedTermBase(TermType type
, String description
, String label
, String labelAbbrev
) {
183 super(type
, description
, label
, labelAbbrev
);
187 //********************** GETTER /SETTER *************************************
190 public String
getIdInVocabulary() {
191 return idInVocabulary
;
195 public void setIdInVocabulary(String idInVocabulary
) {
196 this.idInVocabulary
= idInVocabulary
;
200 public T
getKindOf(){
202 if (this instanceof HibernateProxy
) {
203 HibernateProxy proxy
= (HibernateProxy
) this;
204 LazyInitializer li
= proxy
.getHibernateLazyInitializer();
205 return (T
) ((T
)li
.getImplementation()).getKindOf();
207 return (T
)DefinedTermBase
.deproxy(this.kindOf
, this.getClass());
211 public void setKindOf(T kindOf
){
212 this.kindOf
= kindOf
;
217 public Set
<T
> getGeneralizationOf(){
218 return this.generalizationOf
;
221 protected void setGeneralizationOf(Set
<T
> value
) {
222 this.generalizationOf
= value
;
225 public void addGeneralizationOf(T generalization
) {
226 generalization
.setKindOf(this);
227 this.generalizationOf
.add(generalization
);
231 public void removeGeneralization(T generalization
) {
232 if(generalizationOf
.contains(generalization
)){
233 generalization
.setKindOf(null);
234 this.generalizationOf
.remove(generalization
);
239 public T
getPartOf(){
240 if (this instanceof HibernateProxy
) {
241 HibernateProxy proxy
= (HibernateProxy
) this;
242 LazyInitializer li
= proxy
.getHibernateLazyInitializer();
243 return (T
) ((T
)li
.getImplementation()).getPartOf();
245 return (T
)DefinedTermBase
.deproxy(this.partOf
, this.getClass());
252 public void setPartOf(T partOf
){
253 this.partOf
= partOf
;
257 public Set
<T
> getIncludes(){
258 return this.includes
;
262 * @see #getIncludes()
264 protected void setIncludes(Set
<T
> includes
) {
265 this.includes
= includes
;
269 * @see #getIncludes()
271 public void addIncludes(T includes
) {
272 includes
.setPartOf(this);
273 this.includes
.add(includes
);
277 * @see #getIncludes()
279 public void removeIncludes(T includes
) {
280 if(this.includes
.contains(includes
)) {
281 includes
.setPartOf(null);
282 this.includes
.remove(includes
);
287 public Set
<Media
> getMedia(){
291 public void addMedia(Media media
) {
292 this.media
.add(media
);
294 public void removeMedia(Media media
) {
295 this.media
.remove(media
);
301 public TermVocabulary
<T
> getVocabulary() {
302 return this.vocabulary
;
305 //for bedirectional use only, use vocabulary.addTerm instead
307 * @param newVocabulary
309 protected void setVocabulary(TermVocabulary
<T
> newVocabulary
) {
310 this.vocabulary
= newVocabulary
;
313 //******************************* METHODS ******************************************************/
317 public boolean isKindOf(T ancestor
) {
318 if (kindOf
== null || ancestor
== null){
320 }else if (kindOf
.equals(ancestor
)){
323 return kindOf
.isKindOf(ancestor
);
328 public Set
<T
> getGeneralizationOf(boolean recursive
) {
329 Set
<T
> result
= new HashSet
<T
>();
330 result
.addAll(this.generalizationOf
);
332 for (T child
: this.generalizationOf
){
333 result
.addAll(child
.getGeneralizationOf());
341 public abstract void resetTerms();
343 protected abstract void setDefaultTerms(TermVocabulary
<T
> termVocabulary
);
347 public T
readCsvLine(Class
<T
> termClass
, List
<String
> csvLine
, Map
<UUID
,DefinedTermBase
> terms
, boolean abbrevAsId
) {
349 T newInstance
= getInstance(termClass
);
350 readCsvLine(newInstance
, csvLine
, Language
.CSV_LANGUAGE(), abbrevAsId
);
351 readIsPartOf(newInstance
, csvLine
, terms
);
353 } catch (Exception e
) {
355 for(StackTraceElement ste
: e
.getStackTrace()) {
358 throw new RuntimeException(e
);
362 protected static <TERM
extends DefinedTermBase
> TERM
readCsvLine(TERM newInstance
, List
<String
> csvLine
, Language lang
, boolean abbrevAsId
) {
363 newInstance
.setUuid(UUID
.fromString(csvLine
.get(0)));
364 newInstance
.setUri( URI
.create(csvLine
.get(1)));
365 String label
= csvLine
.get(2).trim();
366 String description
= csvLine
.get(3);
367 String abbreviatedLabel
= csvLine
.get(4);
368 if (StringUtils
.isBlank(abbreviatedLabel
)){
369 abbreviatedLabel
= null;
372 newInstance
.setIdInVocabulary(abbreviatedLabel
); //new in 3.3
374 newInstance
.addRepresentation(Representation
.NewInstance(description
, label
, abbreviatedLabel
, lang
) );
379 protected void readIsPartOf(T newInstance
, List
<String
> csvLine
, Map
<UUID
, DefinedTermBase
> terms
){
380 int index
= partOfCsvLineIndex();
382 String partOfString
= csvLine
.get(index
);
383 if(StringUtils
.isNotBlank(partOfString
)) {
384 UUID partOfUuid
= UUID
.fromString(partOfString
);
385 DefinedTermBase partOf
= terms
.get(partOfUuid
);
386 partOf
.addIncludes(newInstance
);
396 protected int partOfCsvLineIndex() {
401 private <T
extends DefinedTermBase
> T
getInstance(Class
<?
extends DefinedTermBase
> termClass
) {
403 Constructor
<T
> c
= ((Class
<T
>)termClass
).getDeclaredConstructor();
404 c
.setAccessible(true);
405 T termInstance
= c
.newInstance();
407 } catch (Exception e
) {
408 throw new RuntimeException(e
);
413 public void writeCsvLine(CSVWriter writer
, T term
) {
414 String
[] line
= new String
[4];
415 line
[0] = term
.getUuid().toString();
416 line
[1] = term
.getUri().toString();
417 line
[2] = term
.getLabel();
418 line
[3] = term
.getDescription();
419 writer
.writeNext(line
);
423 public T
getByUuid(UUID uuid
){
424 return this.vocabulary
.findTermByUuid(uuid
);
428 //*********************** CLONE ********************************************************/
431 * Clones <i>this</i> DefinedTermBase. This is a shortcut that enables to create
432 * a new instance that differs only slightly from <i>this</i> defined term base by
433 * modifying only some of the attributes.
435 * @see eu.etaxonomy.cdm.model.common.TermBase#clone()
436 * @see java.lang.Object#clone()
439 public Object
clone() {
440 DefinedTermBase result
;
442 result
= (DefinedTermBase
) super.clone();
443 }catch (CloneNotSupportedException e
) {
444 logger
.warn("Object does not implement cloneable");
449 result
.generalizationOf
= new HashSet
<DefinedTermBase
>();
450 for (DefinedTermBase generalizationOf
: this.generalizationOf
){
451 result
.generalizationOf
.add(generalizationOf
.clone());
454 result
.includes
= new HashSet
<DefinedTermBase
>();
456 for (DefinedTermBase include
: this.includes
){
457 result
.includes
.add(include
.clone());
460 result
.media
= new HashSet
<Media
>();
462 for (Media media
: this.media
){
463 result
.addMedia(media
);
469 // Currently the CDM Caching mechanism is only used for caching terms
470 private static ICdmCacher cacher
;
473 * Gets the CDM cacher object
475 * @return the CDM cacher object
477 public static ICdmCacher
getCacher() {
482 * Sets the CDM cacher object
484 * @param cacher the CDM cacher object
486 public static void setCacher(ICdmCacher cacher
) {
487 DefinedTermBase
.cacher
= cacher
;