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
.term
;
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
.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
;
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
;
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
.common
.AnnotationType
;
53 import eu
.etaxonomy
.cdm
.model
.common
.ExtensionType
;
54 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
55 import eu
.etaxonomy
.cdm
.model
.common
.MarkerType
;
56 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
57 import eu
.etaxonomy
.cdm
.model
.description
.MeasurementUnit
;
58 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasure
;
59 import eu
.etaxonomy
.cdm
.model
.description
.TextFormat
;
60 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaType
;
61 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
62 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
63 import eu
.etaxonomy
.cdm
.model
.media
.RightsType
;
64 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalCode
;
65 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEventType
;
66 import eu
.etaxonomy
.cdm
.model
.occurrence
.PreservationMethod
;
70 * workaround for enumerations, base type according to TDWG. For linear ordering
71 * use partOf relation and BreadthFirst. Default iterator order should therefore
72 * be BreadthFirst (not DepthFirst)
74 * @since 08-Nov-2007 13:06:19
76 @XmlAccessorType(XmlAccessType
.FIELD
)
77 @XmlType(name
= "DefinedTermBase", propOrder
= {
84 @XmlRootElement(name
= "DefinedTermBase")
87 DerivationEventType
.class,
93 MeasurementUnit
.class,
95 NomenclaturalCode
.class,
96 PreservationMethod
.class,
97 ReferenceSystem
.class,
99 StatisticalMeasure
.class,
104 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
105 @ClassBridge(impl
=DefinedTermBaseClassBridge
.class)
106 //TODO Comparable implemented only for fixing failing JAXB import, may be removed when this is fixed
107 public abstract class DefinedTermBase
<T
extends DefinedTermBase
>
109 implements IDefinedTerm
<T
>, Comparable
<T
> {
111 private static final long serialVersionUID
= 2931811562248571531L;
112 private static final Logger logger
= Logger
.getLogger(DefinedTermBase
.class);
114 // @XmlElement(name = "KindOf")
116 // @XmlSchemaType(name = "IDREF")
118 @ManyToOne(fetch
= FetchType
.LAZY
, targetEntity
= DefinedTermBase
.class)
119 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
122 * FIXME - Hibernate returns this as a collection of CGLibProxy$$DefinedTermBase objects
123 * which can't be cast to instances of T - can we explicitly initialize these terms using
124 * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?
126 // @XmlElementWrapper(name = "Generalizations")
127 // @XmlElement(name = "GeneralizationOf")
129 // @XmlSchemaType(name = "IDREF")
131 @OneToMany(fetch
=FetchType
.LAZY
, mappedBy
= "kindOf", targetEntity
= DefinedTermBase
.class)
132 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
133 private Set
<T
> generalizationOf
= new HashSet
<>();
135 // @XmlElement(name = "PartOf")
137 // @XmlSchemaType(name = "IDREF")
139 @ManyToOne(fetch
= FetchType
.LAZY
, targetEntity
= DefinedTermBase
.class)
140 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
144 * FIXME - Hibernate retuns this as a collection of CGLibProxy$$DefinedTermBase objects
145 * which can't be cast to instances of T - can we explicitly initialize these terms using
146 * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?
148 // @XmlElementWrapper(name = "Includes")
149 // @XmlElement(name = "Include")
151 // @XmlSchemaType(name = "IDREF")
153 @OneToMany(fetch
=FetchType
.LAZY
, mappedBy
= "partOf", targetEntity
= DefinedTermBase
.class)
154 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
155 private Set
<T
> includes
= new HashSet
<>();
157 @XmlElementWrapper(name
= "Media")
158 @XmlElement(name
= "Medium")
160 @XmlSchemaType(name
= "IDREF")
161 @ManyToMany(fetch
= FetchType
.LAZY
)
162 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
163 private Set
<Media
> media
= new HashSet
<>();
165 @XmlElement(name
= "TermVocabulary")
167 @XmlSchemaType(name
= "IDREF")
168 @ManyToOne(fetch
=FetchType
.LAZY
)
169 // @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE}) remove cascading #5754
170 protected TermVocabulary
<T
> vocabulary
;
172 //the unique identifier/name this term uses in its given vocabulary #3479
173 @XmlElement(name
= "idInVocabulary")
175 //TODO Val #3379, #4245
177 private String idInVocabulary
; //the unique identifier/name this term uses in its given vocabulary #3479
179 @XmlElement(name
= "symbol")
181 //the symbol to be used in String representations for this term #5734
182 //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
183 //empty string is explicitly allowed and should be distinguished from NULL!
184 private String symbol
;
186 @XmlElement(name
= "symbol2")
188 //the second symbol to be used in String representations for this term #7096
189 //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
190 //empty string is explicitly allowed and should be distinguished from NULL!
191 private String symbol2
;
193 //***************************** CONSTRUCTOR *******************************************/
197 protected DefinedTermBase(){};
199 protected DefinedTermBase(TermType type
) {
202 public DefinedTermBase(TermType type
, String description
, String label
, String labelAbbrev
) {
203 super(type
, description
, label
, labelAbbrev
);
207 //********************** GETTER /SETTER *************************************
210 public String
getIdInVocabulary() {
211 return idInVocabulary
;
215 public void setIdInVocabulary(String idInVocabulary
) {
217 this.idInVocabulary
= CdmUtils
.isBlank(idInVocabulary
)?
null : idInVocabulary
;
221 public T
getKindOf(){
223 if (this instanceof HibernateProxy
) {
224 HibernateProxy proxy
= (HibernateProxy
) this;
225 LazyInitializer li
= proxy
.getHibernateLazyInitializer();
226 return (T
)((T
)li
.getImplementation()).getKindOf();
228 return (T
)DefinedTermBase
.deproxy(this.kindOf
, this.getClass());
232 public void setKindOf(T kindOf
){
233 this.kindOf
= kindOf
;
238 public Set
<T
> getGeneralizationOf(){
239 return this.generalizationOf
;
242 protected void setGeneralizationOf(Set
<T
> value
) {
243 this.generalizationOf
= value
;
246 public void addGeneralizationOf(T generalization
) {
247 checkTermType(generalization
);
248 generalization
.setKindOf(this);
249 this.generalizationOf
.add(generalization
);
253 public void removeGeneralization(T generalization
) {
254 if(generalizationOf
.contains(generalization
)){
255 generalization
.setKindOf(null);
256 this.generalizationOf
.remove(generalization
);
261 public T
getPartOf(){
262 if (this instanceof HibernateProxy
) {
263 HibernateProxy proxy
= (HibernateProxy
) this;
264 LazyInitializer li
= proxy
.getHibernateLazyInitializer();
265 return (T
)((T
)li
.getImplementation()).getPartOf();
267 return (T
)DefinedTermBase
.deproxy(this.partOf
, this.getClass());
274 public void setPartOf(T partOf
){
275 this.partOf
= partOf
;
279 //TODO Comparable implemented only for fixing failing JAXB imports, may be removed when this is fixed
281 @Deprecated //for inner use only
282 public int compareTo(T other
) {
283 return ((Integer
)this.getId()).compareTo(other
.getId());
287 public Set
<T
> getIncludes(){
288 return this.includes
;
292 * @see #getIncludes()
294 protected void setIncludes(Set
<T
> includes
) {
295 this.includes
= includes
;
299 * @see #getIncludes()
301 public void addIncludes(T includes
) {
302 checkTermType(includes
);
303 includes
.setPartOf(this);
304 this.includes
.add(includes
);
308 * @see #getIncludes()
310 public void removeIncludes(T includes
) {
311 if(this.includes
.contains(includes
)) {
312 includes
.setPartOf(null);
313 this.includes
.remove(includes
);
318 public Set
<Media
> getMedia(){
322 public void addMedia(Media media
) {
323 this.media
.add(media
);
325 public void removeMedia(Media media
) {
326 this.media
.remove(media
);
332 public TermVocabulary
<T
> getVocabulary() {
333 return this.vocabulary
;
336 //for bedirectional use only, use vocabulary.addTerm instead
338 * @param newVocabulary
340 protected void setVocabulary(TermVocabulary
<T
> newVocabulary
) {
341 this.vocabulary
= newVocabulary
;
345 public String
getSymbol() {
348 public void setSymbol(String symbol
) {
349 this.symbol
= symbol
;
352 * @return the symbol2
354 public String
getSymbol2() {
359 * @param symbol2 the symbol2 to set
361 public void setSymbol2(String symbol2
) {
362 this.symbol2
= symbol2
;
365 //******************************* METHODS ******************************************************/
370 public boolean isKindOf(T ancestor
) {
371 if (kindOf
== null || ancestor
== null){
373 }else if (kindOf
.equals(ancestor
)){
376 return kindOf
.isKindOf(ancestor
);
381 public Set
<T
> getGeneralizationOf(boolean recursive
) {
382 Set
<T
> result
= new HashSet
<T
>();
383 result
.addAll(this.generalizationOf
);
385 for (T child
: this.generalizationOf
){
386 result
.addAll(child
.getGeneralizationOf());
394 public abstract void resetTerms();
396 protected abstract void setDefaultTerms(TermVocabulary
<T
> termVocabulary
);
400 public T
readCsvLine(Class
<T
> termClass
, List
<String
> csvLine
, TermType termType
, Map
<UUID
,DefinedTermBase
> terms
, boolean abbrevAsId
) {
402 T newInstance
= getInstance(termClass
, termType
);
403 readCsvLine(newInstance
, csvLine
, Language
.CSV_LANGUAGE(), abbrevAsId
);
404 readIsPartOf(newInstance
, csvLine
, terms
);
406 } catch (Exception e
) {
408 for(StackTraceElement ste
: e
.getStackTrace()) {
411 throw new RuntimeException(e
);
415 protected static <TERM
extends DefinedTermBase
> TERM
readCsvLine(TERM newInstance
, List
<String
> csvLine
, Language lang
, boolean abbrevAsId
) {
416 newInstance
.setUuid(UUID
.fromString(csvLine
.get(0)));
417 String uriStr
= CdmUtils
.Ne(csvLine
.get(1));
418 newInstance
.setUri(uriStr
== null?
null: URI
.create(uriStr
));
419 String label
= csvLine
.get(2).trim();
420 String description
= CdmUtils
.Ne(csvLine
.get(3).trim());
421 String abbreviatedLabel
= CdmUtils
.Ne(csvLine
.get(4).trim());
422 if (CdmUtils
.isBlank(abbreviatedLabel
)){
423 abbreviatedLabel
= null;
426 newInstance
.setIdInVocabulary(abbreviatedLabel
); //new in 3.3
428 newInstance
.addRepresentation(Representation
.NewInstance(description
, label
, abbreviatedLabel
, lang
) );
433 protected void readIsPartOf(T newInstance
, List
<String
> csvLine
, Map
<UUID
, DefinedTermBase
> terms
){
434 int index
= partOfCsvLineIndex();
436 String partOfString
= csvLine
.get(index
);
437 if(CdmUtils
.isNotBlank(partOfString
)) {
438 UUID partOfUuid
= UUID
.fromString(partOfString
);
439 DefinedTermBase partOf
= terms
.get(partOfUuid
);
440 partOf
.addIncludes(newInstance
);
450 protected int partOfCsvLineIndex() {
455 private <T
extends DefinedTermBase
> T
getInstance(Class
<?
extends DefinedTermBase
> termClass
, TermType termType
) {
457 Constructor
<T
> c
= ((Class
<T
>)termClass
).getDeclaredConstructor();
458 c
.setAccessible(true);
459 T termInstance
= c
.newInstance();
460 termInstance
.setTermType(termType
);
462 } catch (Exception e
) {
463 throw new RuntimeException(e
);
468 public void writeCsvLine(CSVWriter writer
, T term
) {
469 String
[] line
= new String
[4];
470 line
[0] = term
.getUuid().toString();
471 line
[1] = term
.getUri().toString();
472 line
[2] = term
.getLabel();
473 line
[3] = term
.getDescription();
474 writer
.writeNext(line
);
478 public T
getByUuid(UUID uuid
){
479 return this.vocabulary
.findTermByUuid(uuid
);
483 * Throws {@link IllegalArgumentException} if the given
484 * term has not the same term type as this term or if term type is null.
487 private void checkTermType(IHasTermType term
) {
488 IHasTermType
.checkTermTypes(term
, this);
492 //*********************** CLONE ********************************************************/
495 * Clones <i>this</i> DefinedTermBase. This is a shortcut that enables to create
496 * a new instance that differs only slightly from <i>this</i> defined term base by
497 * modifying only some of the attributes.
499 * @see eu.etaxonomy.cdm.model.term.TermBase#clone()
500 * @see java.lang.Object#clone()
503 public Object
clone() {
504 DefinedTermBase result
;
506 result
= (DefinedTermBase
) super.clone();
507 }catch (CloneNotSupportedException e
) {
508 logger
.warn("Object does not implement cloneable");
513 result
.generalizationOf
= new HashSet
<DefinedTermBase
>();
514 for (DefinedTermBase generalizationOf
: this.generalizationOf
){
515 result
.generalizationOf
.add(generalizationOf
.clone());
518 result
.includes
= new HashSet
<DefinedTermBase
>();
520 for (DefinedTermBase include
: this.includes
){
521 result
.includes
.add(include
.clone());
524 result
.media
= new HashSet
<Media
>();
526 for (Media media
: this.media
){
527 result
.addMedia(media
);
533 // Currently the CDM Caching mechanism is only used for caching terms
534 private static ICdmUuidCacher cacher
;
538 * Gets the CDM cacher object
540 * @return the CDM cacher object
542 public static ICdmUuidCacher
getCacher() {
547 * Sets the CDM cacher object
549 * @param cacher the CDM cacher object
551 public static void setCacher(ICdmUuidCacher cacher
) {
552 DefinedTermBase
.cacher
= cacher
;
555 public static <T
extends DefinedTermBase
> T
getTermByClassAndUUID(Class
<T
> clazz
, UUID uuid
) {
557 Object obj
= getCacher().load(uuid
);
558 if(obj
!= null && obj
.getClass().equals(clazz
)) {