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
.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
) {
207 public DefinedTermBase(TermType type
, String description
, String label
, String labelAbbrev
) {
208 super(type
, description
, label
, labelAbbrev
);
212 //********************** GETTER /SETTER *************************************
215 public String
getIdInVocabulary() {
216 return idInVocabulary
;
220 public void setIdInVocabulary(String idInVocabulary
) {
222 this.idInVocabulary
= CdmUtils
.isBlank(idInVocabulary
)?
null : idInVocabulary
;
226 public T
getKindOf(){
228 if (this instanceof HibernateProxy
) {
229 HibernateProxy proxy
= (HibernateProxy
) this;
230 LazyInitializer li
= proxy
.getHibernateLazyInitializer();
231 return (T
)((T
)li
.getImplementation()).getKindOf();
233 return (T
)DefinedTermBase
.deproxy(this.kindOf
, this.getClass());
237 public void setKindOf(T kindOf
){
238 this.kindOf
= kindOf
;
243 public Set
<T
> getGeneralizationOf(){
244 return this.generalizationOf
;
247 protected void setGeneralizationOf(Set
<T
> value
) {
248 this.generalizationOf
= value
;
251 public void addGeneralizationOf(T generalization
) {
252 checkTermType(generalization
);
253 generalization
.setKindOf(this);
254 this.generalizationOf
.add(generalization
);
258 public void removeGeneralization(T generalization
) {
259 if(generalizationOf
.contains(generalization
)){
260 generalization
.setKindOf(null);
261 this.generalizationOf
.remove(generalization
);
266 public T
getPartOf(){
267 if (this instanceof HibernateProxy
) {
268 HibernateProxy proxy
= (HibernateProxy
) this;
269 LazyInitializer li
= proxy
.getHibernateLazyInitializer();
270 return (T
)((T
)li
.getImplementation()).getPartOf();
272 return (T
)DefinedTermBase
.deproxy(this.partOf
, this.getClass());
279 public void setPartOf(T partOf
){
280 this.partOf
= partOf
;
284 //TODO Comparable implemented only for fixing failing JAXB imports, may be removed when this is fixed
286 @Deprecated //for inner use only
287 public int compareTo(T other
) {
288 return ((Integer
)this.getId()).compareTo(other
.getId());
292 public Set
<T
> getIncludes(){
293 return this.includes
;
297 * @see #getIncludes()
299 protected void setIncludes(Set
<T
> includes
) {
300 this.includes
= includes
;
304 * @see #getIncludes()
306 public void addIncludes(T includes
) {
307 checkTermType(includes
);
308 includes
.setPartOf(this);
309 this.includes
.add(includes
);
313 * @see #getIncludes()
315 public void removeIncludes(T includes
) {
316 if(this.includes
.contains(includes
)) {
317 includes
.setPartOf(null);
318 this.includes
.remove(includes
);
323 public Set
<Media
> getMedia(){
327 public void addMedia(Media media
) {
328 this.media
.add(media
);
330 public void removeMedia(Media media
) {
331 this.media
.remove(media
);
334 public TermVocabulary
<T
> getVocabulary() {
335 return this.vocabulary
;
338 //for bedirectional use only, use vocabulary.addTerm instead
339 protected void setVocabulary(TermVocabulary
<T
> newVocabulary
) {
340 this.vocabulary
= newVocabulary
;
344 public String
getSymbol() {
347 public void setSymbol(String symbol
) {
348 this.symbol
= symbol
;
351 public String
getSymbol2() {
354 public void setSymbol2(String symbol2
) {
355 this.symbol2
= symbol2
;
358 //******************************* METHODS ******************************************************/
361 public boolean isKindOf(T ancestor
) {
362 if (kindOf
== null || ancestor
== null){
364 }else if (kindOf
.equals(ancestor
)){
367 return kindOf
.isKindOf(ancestor
);
372 public Set
<T
> getGeneralizationOf(boolean recursive
) {
373 Set
<T
> result
= new HashSet
<T
>();
374 result
.addAll(this.generalizationOf
);
376 for (T child
: this.generalizationOf
){
377 result
.addAll(child
.getGeneralizationOf());
383 public abstract void resetTerms();
385 protected abstract void setDefaultTerms(TermVocabulary
<T
> termVocabulary
);
389 public T
readCsvLine(Class
<T
> termClass
, List
<String
> csvLine
, TermType termType
, Map
<UUID
,DefinedTermBase
> terms
, boolean abbrevAsId
) {
391 T newInstance
= getInstance(termClass
, termType
);
392 readCsvLine(newInstance
, csvLine
, Language
.CSV_LANGUAGE(), abbrevAsId
);
393 readIsPartOf(newInstance
, csvLine
, terms
);
395 } catch (Exception e
) {
397 for(StackTraceElement ste
: e
.getStackTrace()) {
400 throw new RuntimeException(e
);
404 protected static <TERM
extends DefinedTermBase
> TERM
readCsvLine(TERM newInstance
, List
<String
> csvLine
, Language lang
, boolean abbrevAsId
) {
405 newInstance
.setUuid(UUID
.fromString(csvLine
.get(0)));
406 String uriStr
= CdmUtils
.Ne(csvLine
.get(1));
407 newInstance
.setUri(uriStr
== null?
null: URI
.create(uriStr
));
408 String label
= csvLine
.get(2).trim();
409 String description
= CdmUtils
.Ne(csvLine
.get(3).trim());
410 String abbreviatedLabel
= CdmUtils
.Ne(csvLine
.get(4).trim());
411 if (CdmUtils
.isBlank(abbreviatedLabel
)){
412 abbreviatedLabel
= null;
415 newInstance
.setIdInVocabulary(abbreviatedLabel
); //new in 3.3
417 newInstance
.addRepresentation(Representation
.NewInstance(description
, label
, abbreviatedLabel
, lang
) );
422 protected void readIsPartOf(T newInstance
, List
<String
> csvLine
, Map
<UUID
, DefinedTermBase
> terms
){
423 int index
= partOfCsvLineIndex();
425 String partOfString
= csvLine
.get(index
);
426 if(CdmUtils
.isNotBlank(partOfString
)) {
427 UUID partOfUuid
= UUID
.fromString(partOfString
);
428 DefinedTermBase partOf
= terms
.get(partOfUuid
);
429 partOf
.addIncludes(newInstance
);
434 protected int partOfCsvLineIndex() {
438 private <T
extends DefinedTermBase
> T
getInstance(Class
<?
extends DefinedTermBase
> termClass
, TermType termType
) {
440 Constructor
<T
> c
= ((Class
<T
>)termClass
).getDeclaredConstructor();
441 c
.setAccessible(true);
442 T termInstance
= c
.newInstance();
443 termInstance
.setTermType(termType
);
445 } catch (Exception e
) {
446 throw new RuntimeException(e
);
451 public void writeCsvLine(CSVWriter writer
, T term
) {
452 String
[] line
= new String
[4];
453 line
[0] = term
.getUuid().toString();
454 line
[1] = term
.getUri().toString();
455 line
[2] = term
.getLabel();
456 line
[3] = term
.getDescription();
457 writer
.writeNext(line
);
461 public T
getByUuid(UUID uuid
){
462 return this.vocabulary
.findTermByUuid(uuid
);
466 * Throws {@link IllegalArgumentException} if the given
467 * term has not the same term type as this term or if term type is null.
470 private void checkTermType(IHasTermType term
) {
471 IHasTermType
.checkTermTypes(term
, this);
475 //*********************** CLONE ********************************************************/
478 * Clones <i>this</i> DefinedTermBase. This is a shortcut that enables to create
479 * a new instance that differs only slightly from <i>this</i> defined term base by
480 * modifying only some of the attributes.
482 * @see eu.etaxonomy.cdm.model.term.TermBase#clone()
483 * @see java.lang.Object#clone()
486 public DefinedTermBase
<T
> clone() {
488 DefinedTermBase
<T
> result
= (DefinedTermBase
<T
>) super.clone();
490 result
.generalizationOf
= new HashSet
<>();
491 for (DefinedTermBase
<T
> generalizationOf
: this.generalizationOf
){
492 result
.generalizationOf
.add((T
)generalizationOf
.clone());
495 result
.includes
= new HashSet
<>();
497 for (DefinedTermBase
<?
> include
: this.includes
){
498 result
.includes
.add((T
)include
.clone());
501 result
.media
= new HashSet
<>();
503 for (Media media
: this.media
){
504 result
.addMedia(media
);
508 }catch (CloneNotSupportedException e
) {
509 logger
.warn("Object does not implement cloneable");
515 // Currently the CDM Caching mechanism is only used for caching terms
516 private static ICdmUuidCacher cacher
;
520 * Gets the CDM cacher object
522 * @return the CDM cacher object
524 public static ICdmUuidCacher
getCacher() {
529 * Sets the CDM cacher object
531 * @param cacher the CDM cacher object
533 public static void setCacher(ICdmUuidCacher cacher
) {
534 DefinedTermBase
.cacher
= cacher
;
537 public static <T
extends DefinedTermBase
> T
getTermByClassAndUUID(Class
<T
> clazz
, UUID uuid
) {
539 Object obj
= getCacher().load(uuid
);
540 if(obj
!= null && obj
.getClass().equals(clazz
)) {