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
;
13 import java
.util
.HashSet
;
14 import java
.util
.List
;
17 import java
.util
.UUID
;
19 import javax
.persistence
.Column
;
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
.hibernate
.annotations
.Cascade
;
41 import org
.hibernate
.annotations
.CascadeType
;
42 import org
.hibernate
.envers
.Audited
;
43 import org
.hibernate
.proxy
.HibernateProxy
;
44 import org
.hibernate
.proxy
.LazyInitializer
;
45 import org
.hibernate
.search
.annotations
.ClassBridge
;
47 import au
.com
.bytecode
.opencsv
.CSVWriter
;
48 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
49 import eu
.etaxonomy
.cdm
.common
.URI
;
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
.ExternallyManaged
;
55 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
56 import eu
.etaxonomy
.cdm
.model
.common
.MarkerType
;
57 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
58 import eu
.etaxonomy
.cdm
.model
.description
.MeasurementUnit
;
59 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasure
;
60 import eu
.etaxonomy
.cdm
.model
.description
.TextFormat
;
61 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaType
;
62 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
63 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
64 import eu
.etaxonomy
.cdm
.model
.media
.RightsType
;
65 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalCode
;
66 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEventType
;
67 import eu
.etaxonomy
.cdm
.model
.occurrence
.PreservationMethod
;
71 * workaround for enumerations, base type according to TDWG. For linear ordering
72 * use partOf relation and BreadthFirst. Default iterator order should therefore
73 * be BreadthFirst (not DepthFirst)
75 * @since 08-Nov-2007 13:06:19
77 @XmlAccessorType(XmlAccessType
.FIELD
)
78 @XmlType(name
= "DefinedTermBase", propOrder
= {
86 @XmlRootElement(name
= "DefinedTermBase")
89 DerivationEventType
.class,
95 MeasurementUnit
.class,
97 NomenclaturalCode
.class,
98 PreservationMethod
.class,
99 ReferenceSystem
.class,
101 StatisticalMeasure
.class,
106 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
107 @ClassBridge(impl
=DefinedTermBaseClassBridge
.class)
108 //TODO Comparable implemented only for fixing failing JAXB import, may be removed when this is fixed
109 public abstract class DefinedTermBase
<T
extends DefinedTermBase
>
111 implements IDefinedTerm
<T
>, Comparable
<T
> {
113 private static final long serialVersionUID
= 2931811562248571531L;
114 private static final Logger logger
= Logger
.getLogger(DefinedTermBase
.class);
116 // @XmlElement(name = "KindOf")
118 // @XmlSchemaType(name = "IDREF")
120 @ManyToOne(fetch
= FetchType
.LAZY
, targetEntity
= DefinedTermBase
.class)
121 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
125 * FIXME - Hibernate returns this as a collection of CGLibProxy$$DefinedTermBase objects
126 * which can't be cast to instances of T - can we explicitly initialize these terms using
127 * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?
129 // @XmlElementWrapper(name = "Generalizations")
130 // @XmlElement(name = "GeneralizationOf")
132 // @XmlSchemaType(name = "IDREF")
134 @OneToMany(fetch
=FetchType
.LAZY
, mappedBy
= "kindOf", targetEntity
= DefinedTermBase
.class)
135 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
136 private Set
<T
> generalizationOf
= new HashSet
<>();
138 // @XmlElement(name = "PartOf")
140 // @XmlSchemaType(name = "IDREF")
142 @ManyToOne(fetch
= FetchType
.LAZY
, targetEntity
= DefinedTermBase
.class)
143 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
147 * FIXME - Hibernate retuns this as a collection of CGLibProxy$$DefinedTermBase objects
148 * which can't be cast to instances of T - can we explicitly initialize these terms using
149 * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?
151 // @XmlElementWrapper(name = "Includes")
152 // @XmlElement(name = "Include")
154 // @XmlSchemaType(name = "IDREF")
156 @OneToMany(fetch
=FetchType
.LAZY
, mappedBy
= "partOf", targetEntity
= DefinedTermBase
.class)
157 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
158 private Set
<T
> includes
= new HashSet
<>();
160 @XmlElementWrapper(name
= "Media")
161 @XmlElement(name
= "Medium")
163 @XmlSchemaType(name
= "IDREF")
164 @ManyToMany(fetch
= FetchType
.LAZY
)
165 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
166 private Set
<Media
> media
= new HashSet
<>();
168 @XmlElement(name
= "TermVocabulary")
170 @XmlSchemaType(name
= "IDREF")
171 @ManyToOne(fetch
=FetchType
.LAZY
)
172 // @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE}) remove cascading #5754
173 protected TermVocabulary
<T
> vocabulary
;
175 //the unique identifier/name this term uses in its given vocabulary #3479
176 @XmlElement(name
= "idInVocabulary")
178 //TODO Val #3379, #4245
180 private String idInVocabulary
; //the unique identifier/name this term uses in its given vocabulary #3479
182 @XmlElement(name
= "symbol")
184 //the symbol to be used in String representations for this term #5734
185 //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
186 //empty string is explicitly allowed and should be distinguished from NULL!
187 private String symbol
;
189 @XmlElement(name
= "symbol2")
191 //the second symbol to be used in String representations for this term #7096
192 //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
193 //empty string is explicitly allowed and should be distinguished from NULL!
194 private String symbol2
;
196 private ExternallyManaged externallyManaged
;
198 //***************************** CONSTRUCTOR *******************************************/
202 protected DefinedTermBase(){};
204 protected DefinedTermBase(TermType type
) {
208 public DefinedTermBase(TermType type
, String description
, String label
, String labelAbbrev
) {
209 super(type
, description
, label
, labelAbbrev
);
212 //********************** GETTER /SETTER *************************************
215 public String
getIdInVocabulary() {
216 return idInVocabulary
;
220 public void setIdInVocabulary(String idInVocabulary
) {
221 this.idInVocabulary
= CdmUtils
.isBlank(idInVocabulary
)?
null : idInVocabulary
;
225 public T
getKindOf(){
226 if (this instanceof HibernateProxy
) {
227 HibernateProxy proxy
= (HibernateProxy
) this;
228 LazyInitializer li
= proxy
.getHibernateLazyInitializer();
229 return (T
)((T
)li
.getImplementation()).getKindOf();
231 return (T
)DefinedTermBase
.deproxy(this.kindOf
, this.getClass());
235 public void setKindOf(T kindOf
){
236 this.kindOf
= kindOf
;
240 public Set
<T
> getGeneralizationOf(){
241 return this.generalizationOf
;
243 protected void setGeneralizationOf(Set
<T
> value
) {
244 this.generalizationOf
= value
;
246 public void addGeneralizationOf(T generalization
) {
247 checkTermType(generalization
);
248 generalization
.setKindOf(this);
249 this.generalizationOf
.add(generalization
);
251 public void removeGeneralization(T generalization
) {
252 if(generalizationOf
.contains(generalization
)){
253 generalization
.setKindOf(null);
254 this.generalizationOf
.remove(generalization
);
259 public T
getPartOf(){
260 if (this instanceof HibernateProxy
) {
261 HibernateProxy proxy
= (HibernateProxy
) this;
262 LazyInitializer li
= proxy
.getHibernateLazyInitializer();
263 return (T
)((T
)li
.getImplementation()).getPartOf();
265 return (T
)DefinedTermBase
.deproxy(this.partOf
, this.getClass());
272 public void setPartOf(T partOf
){
273 this.partOf
= partOf
;
277 //TODO Comparable implemented only for fixing failing JAXB imports, may be removed when this is fixed
279 @Deprecated //for inner use only
280 public int compareTo(T other
) {
281 return ((Integer
)this.getId()).compareTo(other
.getId());
285 public Set
<T
> getIncludes(){
286 return this.includes
;
290 * @see #getIncludes()
292 protected void setIncludes(Set
<T
> includes
) {
293 this.includes
= includes
;
297 * @see #getIncludes()
299 public void addIncludes(T includes
) {
300 checkTermType(includes
);
301 includes
.setPartOf(this);
302 this.includes
.add(includes
);
306 * @see #getIncludes()
308 public void removeIncludes(T includes
) {
309 if(this.includes
.contains(includes
)) {
310 includes
.setPartOf(null);
311 this.includes
.remove(includes
);
316 public Set
<Media
> getMedia(){
320 public void addMedia(Media media
) {
321 this.media
.add(media
);
323 public void removeMedia(Media media
) {
324 this.media
.remove(media
);
327 public TermVocabulary
<T
> getVocabulary() {
328 return this.vocabulary
;
331 //for bedirectional use only, use vocabulary.addTerm instead
332 protected void setVocabulary(TermVocabulary
<T
> newVocabulary
) {
333 this.vocabulary
= newVocabulary
;
336 public String
getSymbol() {
339 public void setSymbol(String symbol
) {
340 this.symbol
= symbol
;
343 public String
getSymbol2() {
346 public void setSymbol2(String symbol2
) {
347 this.symbol2
= symbol2
;
350 //******************************* METHODS ******************************************************/
353 public boolean isKindOf(T ancestor
) {
354 if (kindOf
== null || ancestor
== null){
356 }else if (kindOf
.equals(ancestor
)){
359 return kindOf
.isKindOf(ancestor
);
364 public Set
<T
> getGeneralizationOf(boolean recursive
) {
365 Set
<T
> result
= new HashSet
<T
>();
366 result
.addAll(this.generalizationOf
);
368 for (T child
: this.generalizationOf
){
369 result
.addAll(child
.getGeneralizationOf());
375 public abstract void resetTerms();
377 protected abstract void setDefaultTerms(TermVocabulary
<T
> termVocabulary
);
381 public T
readCsvLine(Class
<T
> termClass
, List
<String
> csvLine
, TermType termType
, Map
<UUID
,DefinedTermBase
> terms
, boolean abbrevAsId
) {
383 T newInstance
= getInstance(termClass
, termType
);
384 readCsvLine(newInstance
, csvLine
, Language
.CSV_LANGUAGE(), abbrevAsId
);
385 readIsPartOf(newInstance
, csvLine
, terms
);
387 } catch (Exception e
) {
389 for(StackTraceElement ste
: e
.getStackTrace()) {
392 throw new RuntimeException(e
);
396 protected static <TERM
extends DefinedTermBase
> TERM
readCsvLine(TERM newInstance
, List
<String
> csvLine
, Language lang
, boolean abbrevAsId
) {
397 newInstance
.setUuid(UUID
.fromString(csvLine
.get(0)));
398 String uriStr
= CdmUtils
.Ne(csvLine
.get(1));
399 newInstance
.setUri(uriStr
== null?
null: URI
.create(uriStr
));
400 String label
= csvLine
.get(2).trim();
401 String description
= CdmUtils
.Ne(csvLine
.get(3).trim());
402 String abbreviatedLabel
= CdmUtils
.Ne(csvLine
.get(4).trim());
403 if (CdmUtils
.isBlank(abbreviatedLabel
)){
404 abbreviatedLabel
= null;
407 newInstance
.setIdInVocabulary(abbreviatedLabel
); //new in 3.3
409 newInstance
.addRepresentation(Representation
.NewInstance(description
, label
, abbreviatedLabel
, lang
) );
414 protected void readIsPartOf(T newInstance
, List
<String
> csvLine
, Map
<UUID
, DefinedTermBase
> terms
){
415 int index
= partOfCsvLineIndex();
417 String partOfString
= csvLine
.get(index
);
418 if(CdmUtils
.isNotBlank(partOfString
)) {
419 UUID partOfUuid
= UUID
.fromString(partOfString
);
420 DefinedTermBase partOf
= terms
.get(partOfUuid
);
421 partOf
.addIncludes(newInstance
);
426 protected int partOfCsvLineIndex() {
430 private <T
extends DefinedTermBase
> T
getInstance(Class
<?
extends DefinedTermBase
> termClass
, TermType termType
) {
432 Constructor
<T
> c
= ((Class
<T
>)termClass
).getDeclaredConstructor();
433 c
.setAccessible(true);
434 T termInstance
= c
.newInstance();
435 termInstance
.setTermType(termType
);
437 } catch (Exception e
) {
438 throw new RuntimeException(e
);
443 public void writeCsvLine(CSVWriter writer
, T term
) {
444 String
[] line
= new String
[4];
445 line
[0] = term
.getUuid().toString();
446 line
[1] = term
.getUri().toString();
447 line
[2] = term
.getLabel();
448 line
[3] = term
.getDescription();
449 writer
.writeNext(line
);
453 public T
getByUuid(UUID uuid
){
454 return this.vocabulary
.findTermByUuid(uuid
);
458 * Throws {@link IllegalArgumentException} if the given
459 * term has not the same term type as this term or if term type is null.
462 private void checkTermType(IHasTermType term
) {
463 IHasTermType
.checkTermTypes(term
, this);
467 //*********************** CLONE ********************************************************/
470 * Clones <i>this</i> DefinedTermBase. This is a shortcut that enables to create
471 * a new instance that differs only slightly from <i>this</i> defined term base by
472 * modifying only some of the attributes.
474 * @see eu.etaxonomy.cdm.model.term.TermBase#clone()
475 * @see java.lang.Object#clone()
478 public DefinedTermBase
<T
> clone() {
480 DefinedTermBase
<T
> result
= (DefinedTermBase
<T
>) super.clone();
482 result
.generalizationOf
= new HashSet
<>();
483 for (DefinedTermBase
<T
> generalizationOf
: this.generalizationOf
){
484 result
.generalizationOf
.add((T
)generalizationOf
.clone());
487 result
.includes
= new HashSet
<>();
489 for (DefinedTermBase
<?
> include
: this.includes
){
490 result
.includes
.add((T
)include
.clone());
493 result
.media
= new HashSet
<>();
495 for (Media media
: this.media
){
496 result
.addMedia(media
);
500 }catch (CloneNotSupportedException e
) {
501 logger
.warn("Object does not implement cloneable");
507 // Currently the CDM Caching mechanism is only used for caching terms
508 private static ICdmUuidCacher cacher
;
512 * Gets the CDM cacher object
514 * @return the CDM cacher object
516 public static ICdmUuidCacher
getCacher() {
521 * Sets the CDM cacher object
523 * @param cacher the CDM cacher object
525 public static void setCacher(ICdmUuidCacher cacher
) {
526 DefinedTermBase
.cacher
= cacher
;
529 public static <T
extends DefinedTermBase
> T
getTermByClassAndUUID(Class
<T
> clazz
, UUID uuid
) {
531 Object obj
= getCacher().load(uuid
);
532 if(obj
!= null && obj
.getClass().equals(clazz
)) {