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
.logging
.log4j
.LogManager
;
40 import org
.apache
.logging
.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
.common
.URI
;
51 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
52 import eu
.etaxonomy
.cdm
.hibernate
.search
.DefinedTermBaseClassBridge
;
53 import eu
.etaxonomy
.cdm
.model
.ICdmUuidCacher
;
54 import eu
.etaxonomy
.cdm
.model
.common
.AnnotationType
;
55 import eu
.etaxonomy
.cdm
.model
.common
.ExtensionType
;
56 import eu
.etaxonomy
.cdm
.model
.common
.ExternallyManaged
;
57 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
58 import eu
.etaxonomy
.cdm
.model
.common
.MarkerType
;
59 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
60 import eu
.etaxonomy
.cdm
.model
.description
.MeasurementUnit
;
61 import eu
.etaxonomy
.cdm
.model
.description
.StatisticalMeasure
;
62 import eu
.etaxonomy
.cdm
.model
.description
.TextFormat
;
63 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaType
;
64 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
65 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
66 import eu
.etaxonomy
.cdm
.model
.media
.RightsType
;
67 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalCode
;
68 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEventType
;
69 import eu
.etaxonomy
.cdm
.model
.occurrence
.PreservationMethod
;
73 * workaround for enumerations, base type according to TDWG. For linear ordering
74 * use partOf relation and BreadthFirst. Default iterator order should therefore
75 * be BreadthFirst (not DepthFirst)
77 * @since 08-Nov-2007 13:06:19
79 @XmlAccessorType(XmlAccessType
.FIELD
)
80 @XmlType(name
= "DefinedTermBase", propOrder
= {
88 @XmlRootElement(name
= "DefinedTermBase")
91 DerivationEventType
.class,
97 MeasurementUnit
.class,
99 NomenclaturalCode
.class,
100 PreservationMethod
.class,
101 ReferenceSystem
.class,
103 StatisticalMeasure
.class,
108 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
109 @ClassBridge(impl
=DefinedTermBaseClassBridge
.class)
110 //TODO Comparable implemented only for fixing failing JAXB import, may be removed when this is fixed
111 public abstract class DefinedTermBase
<T
extends DefinedTermBase
>
113 implements IDefinedTerm
<T
>, Comparable
<T
> {
115 private static final long serialVersionUID
= 2931811562248571531L;
116 private static final Logger logger
= LogManager
.getLogger(DefinedTermBase
.class);
118 // @XmlElement(name = "KindOf")
120 // @XmlSchemaType(name = "IDREF")
122 @ManyToOne(fetch
= FetchType
.LAZY
, targetEntity
= DefinedTermBase
.class)
123 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
127 * FIXME - Hibernate returns this as a collection of CGLibProxy$$DefinedTermBase objects
128 * which can't be cast to instances of T - can we explicitly initialize these terms using
129 * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?
131 // @XmlElementWrapper(name = "Generalizations")
132 // @XmlElement(name = "GeneralizationOf")
134 // @XmlSchemaType(name = "IDREF")
136 @OneToMany(fetch
=FetchType
.LAZY
, mappedBy
= "kindOf", targetEntity
= DefinedTermBase
.class)
137 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
138 private Set
<T
> generalizationOf
= new HashSet
<>();
140 // @XmlElement(name = "PartOf")
142 // @XmlSchemaType(name = "IDREF")
144 @ManyToOne(fetch
= FetchType
.LAZY
, targetEntity
= DefinedTermBase
.class)
145 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
149 * FIXME - Hibernate retuns this as a collection of CGLibProxy$$DefinedTermBase objects
150 * which can't be cast to instances of T - can we explicitly initialize these terms using
151 * Hibernate.initialize(), does this imply a distinct load, and find methods in the dao?
153 // @XmlElementWrapper(name = "Includes")
154 // @XmlElement(name = "Include")
156 // @XmlSchemaType(name = "IDREF")
158 @OneToMany(fetch
=FetchType
.LAZY
, mappedBy
= "partOf", targetEntity
= DefinedTermBase
.class)
159 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
160 private Set
<T
> includes
= new HashSet
<>();
162 @XmlElementWrapper(name
= "Media")
163 @XmlElement(name
= "Medium")
165 @XmlSchemaType(name
= "IDREF")
166 @ManyToMany(fetch
= FetchType
.LAZY
)
167 @Cascade({CascadeType
.SAVE_UPDATE
,CascadeType
.MERGE
})
168 private Set
<Media
> media
= new HashSet
<>();
170 @XmlElement(name
= "TermVocabulary")
172 @XmlSchemaType(name
= "IDREF")
173 @ManyToOne(fetch
=FetchType
.LAZY
)
174 // @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE}) remove cascading #5754
175 protected TermVocabulary
<T
> vocabulary
;
177 //the unique identifier/name this term uses in its given vocabulary #3479
178 @XmlElement(name
= "idInVocabulary")
180 //TODO Val #3379, #4245
182 private String idInVocabulary
; //the unique identifier/name this term uses in its given vocabulary #3479
184 @XmlElement(name
= "symbol")
186 //the symbol to be used in String representations for this term #5734
187 //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
188 //empty string is explicitly allowed and should be distinguished from NULL!
189 private String symbol
;
191 @XmlElement(name
= "symbol2")
193 //the second symbol to be used in String representations for this term #7096
194 //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
195 //empty string is explicitly allowed and should be distinguished from NULL!
196 private String symbol2
;
198 private ExternallyManaged externallyManaged
;
200 //***************************** CONSTRUCTOR *******************************************/
202 //for hibernate use only, *packet* private required by bytebuddy
203 //2022-06-17: currently still needed protected as TaxEditor.TaxonRelationshipTypeInverseContainer inherits from DefinedTermBase
205 protected DefinedTermBase(){}
207 protected DefinedTermBase(TermType type
) {
211 public DefinedTermBase(TermType type
, String description
, String label
, String labelAbbrev
, Language lang
) {
212 super(type
, description
, label
, labelAbbrev
, lang
);
214 public DefinedTermBase(TermType type
, String description
, String label
, String labelAbbrev
) {
215 super(type
, description
, label
, labelAbbrev
, null);
218 //********************** GETTER /SETTER *************************************
221 public String
getIdInVocabulary() {
222 return idInVocabulary
;
226 public void setIdInVocabulary(String idInVocabulary
) {
227 this.idInVocabulary
= CdmUtils
.isBlank(idInVocabulary
)?
null : idInVocabulary
;
231 public T
getKindOf(){
232 if (this instanceof HibernateProxy
) {
233 HibernateProxy proxy
= (HibernateProxy
) this;
234 LazyInitializer li
= proxy
.getHibernateLazyInitializer();
235 return (T
)((T
)li
.getImplementation()).getKindOf();
237 return (T
)DefinedTermBase
.deproxy(this.kindOf
, this.getClass());
241 public void setKindOf(T kindOf
){
242 this.kindOf
= kindOf
;
246 public Set
<T
> getGeneralizationOf(){
247 return this.generalizationOf
;
249 protected void setGeneralizationOf(Set
<T
> value
) {
250 this.generalizationOf
= value
;
252 public void addGeneralizationOf(T generalization
) {
253 checkTermType(generalization
);
254 generalization
.setKindOf(this);
255 this.generalizationOf
.add(generalization
);
257 public void removeGeneralization(T generalization
) {
258 if(generalizationOf
.contains(generalization
)){
259 generalization
.setKindOf(null);
260 this.generalizationOf
.remove(generalization
);
265 public T
getPartOf(){
266 if (this instanceof HibernateProxy
) {
267 HibernateProxy proxy
= (HibernateProxy
) this;
268 LazyInitializer li
= proxy
.getHibernateLazyInitializer();
269 return (T
)((T
)li
.getImplementation()).getPartOf();
271 return (T
)DefinedTermBase
.deproxy(this.partOf
, this.getClass());
278 public void setPartOf(T partOf
){
279 this.partOf
= partOf
;
283 //TODO Comparable implemented only for fixing failing JAXB imports, may be removed when this is fixed
285 @Deprecated //for inner use only
286 public int compareTo(T other
) {
287 return ((Integer
)this.getId()).compareTo(other
.getId());
291 public Set
<T
> getIncludes(){
292 return this.includes
;
296 * @see #getIncludes()
298 protected void setIncludes(Set
<T
> includes
) {
299 this.includes
= includes
;
303 * @see #getIncludes()
305 public void addIncludes(T includes
) {
306 checkTermType(includes
);
307 includes
.setPartOf(this);
308 this.includes
.add(includes
);
312 * @see #getIncludes()
314 public void removeIncludes(T includes
) {
315 if(this.includes
.contains(includes
)) {
316 includes
.setPartOf(null);
317 this.includes
.remove(includes
);
322 public Set
<Media
> getMedia(){
326 public void addMedia(Media media
) {
327 this.media
.add(media
);
329 public void removeMedia(Media media
) {
330 this.media
.remove(media
);
333 public TermVocabulary
<T
> getVocabulary() {
334 return this.vocabulary
;
337 //for bedirectional use only, use vocabulary.addTerm instead
338 protected void setVocabulary(TermVocabulary
<T
> newVocabulary
) {
339 this.vocabulary
= newVocabulary
;
342 public String
getSymbol() {
345 public void setSymbol(String symbol
) {
346 this.symbol
= symbol
;
349 public String
getSymbol2() {
352 public void setSymbol2(String symbol2
) {
353 this.symbol2
= symbol2
;
356 //******************************* METHODS ******************************************************/
359 public boolean isKindOf(T ancestor
) {
360 if (kindOf
== null || ancestor
== null){
362 }else if (kindOf
.equals(ancestor
)){
365 return kindOf
.isKindOf(ancestor
);
370 public Set
<T
> getGeneralizationOf(boolean recursive
) {
371 Set
<T
> result
= new HashSet
<T
>();
372 result
.addAll(this.generalizationOf
);
374 for (T child
: this.generalizationOf
){
375 result
.addAll(child
.getGeneralizationOf());
381 public abstract void resetTerms();
383 protected abstract void setDefaultTerms(TermVocabulary
<T
> termVocabulary
);
386 public T
readCsvLine(Class
<T
> termClass
, List
<String
> csvLine
, TermType termType
, Map
<UUID
,DefinedTermBase
> terms
, boolean abbrevAsId
) {
388 T newInstance
= getInstance(termClass
, termType
);
389 readCsvLine(newInstance
, csvLine
, Language
.CSV_LANGUAGE(), abbrevAsId
);
390 readIsPartOf(newInstance
, csvLine
, terms
);
392 } catch (Exception e
) {
394 for(StackTraceElement ste
: e
.getStackTrace()) {
397 throw new RuntimeException(e
);
401 protected static <TERM
extends DefinedTermBase
> TERM
readCsvLine(TERM newInstance
, List
<String
> csvLine
, Language lang
, boolean abbrevAsId
) {
402 newInstance
.setUuid(UUID
.fromString(csvLine
.get(0)));
403 String uriStr
= CdmUtils
.Ne(csvLine
.get(1));
404 newInstance
.setUri(uriStr
== null?
null: URI
.create(uriStr
));
405 String label
= csvLine
.get(2).trim();
406 String description
= CdmUtils
.Ne(csvLine
.get(3).trim());
407 String abbreviatedLabel
= CdmUtils
.Ne(csvLine
.get(4).trim());
408 if (CdmUtils
.isBlank(abbreviatedLabel
)){
409 abbreviatedLabel
= null;
412 newInstance
.setIdInVocabulary(abbreviatedLabel
); //new in 3.3
414 newInstance
.addRepresentation(Representation
.NewInstance(description
, label
, abbreviatedLabel
, lang
) );
419 protected void readIsPartOf(T newInstance
, List
<String
> csvLine
, Map
<UUID
, DefinedTermBase
> terms
){
420 int index
= partOfCsvLineIndex();
422 String partOfString
= csvLine
.get(index
);
423 if(CdmUtils
.isNotBlank(partOfString
)) {
424 UUID partOfUuid
= UUID
.fromString(partOfString
);
425 DefinedTermBase partOf
= terms
.get(partOfUuid
);
426 partOf
.addIncludes(newInstance
);
431 protected int partOfCsvLineIndex() {
435 private <T
extends DefinedTermBase
> T
getInstance(Class
<?
extends DefinedTermBase
> termClass
, TermType termType
) {
437 Constructor
<T
> c
= ((Class
<T
>)termClass
).getDeclaredConstructor();
438 c
.setAccessible(true);
439 T termInstance
= c
.newInstance();
440 termInstance
.setTermType(termType
);
442 } catch (Exception e
) {
443 throw new RuntimeException(e
);
448 public void writeCsvLine(CSVWriter writer
, T term
) {
449 String
[] line
= new String
[4];
450 line
[0] = term
.getUuid().toString();
451 line
[1] = term
.getUri().toString();
452 line
[2] = term
.getLabel();
453 line
[3] = term
.getDescription();
454 writer
.writeNext(line
);
458 public T
getByUuid(UUID uuid
){
459 return this.vocabulary
.findTermByUuid(uuid
);
463 * Throws {@link IllegalArgumentException} if the given
464 * term has not the same term type as this term or if term type is null.
467 private void checkTermType(IHasTermType term
) {
468 IHasTermType
.checkTermTypes(term
, this);
472 //*********************** CLONE ********************************************************/
475 * Clones <i>this</i> DefinedTermBase. This is a shortcut that enables to create
476 * a new instance that differs only slightly from <i>this</i> defined term base by
477 * modifying only some of the attributes.
479 * @see eu.etaxonomy.cdm.model.term.TermBase#clone()
480 * @see java.lang.Object#clone()
483 public DefinedTermBase
<T
> clone() {
485 DefinedTermBase
<T
> result
= (DefinedTermBase
<T
>) super.clone();
487 result
.generalizationOf
= new HashSet
<>();
488 for (DefinedTermBase
<T
> generalizationOf
: this.generalizationOf
){
489 result
.generalizationOf
.add((T
)generalizationOf
.clone());
492 result
.includes
= new HashSet
<>();
494 for (DefinedTermBase
<?
> include
: this.includes
){
495 result
.includes
.add((T
)include
.clone());
498 result
.media
= new HashSet
<>();
500 for (Media media
: this.media
){
501 result
.addMedia(media
);
505 }catch (CloneNotSupportedException e
) {
506 logger
.warn("Object does not implement cloneable");
512 // Currently the CDM Caching mechanism is only used for caching terms
513 private static ICdmUuidCacher cacher
;
517 * Gets the CDM cacher object
519 * @return the CDM cacher object
521 public static ICdmUuidCacher
getCacher() {
526 * Sets the CDM cacher object
528 * @param cacher the CDM cacher object
530 public static void setCacher(ICdmUuidCacher cacher
) {
531 DefinedTermBase
.cacher
= cacher
;
534 public static <T
extends DefinedTermBase
> T
getTermByClassAndUUID(Class
<T
> clazz
, UUID uuid
) {
536 Object obj
= HibernateProxyHelper
.deproxy(getCacher().load(uuid
));
538 if(obj
!= null && obj
.getClass().equals(clazz
)) {