Merge branch 'hotfix/3.12.3' into develop
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / common / TermBase.java
1 /**
2 * Copyright (C) 2009 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9
10 package eu.etaxonomy.cdm.model.common;
11
12 import java.net.URI;
13 import java.util.HashSet;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Set;
17
18 import javax.persistence.Column;
19 import javax.persistence.FetchType;
20 import javax.persistence.MappedSuperclass;
21 import javax.persistence.OneToMany;
22 import javax.persistence.Transient;
23 import javax.validation.constraints.NotNull;
24 import javax.xml.bind.annotation.XmlAccessType;
25 import javax.xml.bind.annotation.XmlAccessorType;
26 import javax.xml.bind.annotation.XmlAttribute;
27 import javax.xml.bind.annotation.XmlElement;
28 import javax.xml.bind.annotation.XmlElementWrapper;
29 import javax.xml.bind.annotation.XmlSeeAlso;
30 import javax.xml.bind.annotation.XmlType;
31
32 import org.apache.log4j.Logger;
33 import org.hibernate.LazyInitializationException;
34 import org.hibernate.annotations.Cascade;
35 import org.hibernate.annotations.CascadeType;
36 import org.hibernate.annotations.Type;
37 import org.hibernate.envers.Audited;
38 import org.hibernate.search.annotations.Analyze;
39 import org.hibernate.search.annotations.Field;
40
41 import eu.etaxonomy.cdm.model.description.TextData;
42 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
43 import eu.etaxonomy.cdm.strategy.cache.common.TermDefaultCacheStrategy;
44
45 @XmlAccessorType(XmlAccessType.FIELD)
46 @XmlType(name = "TermBase", propOrder = {
47 "uri",
48 "termType",
49 "representations"
50 })
51 @XmlSeeAlso({
52 DefinedTermBase.class,
53 TermVocabulary.class
54 })
55 @MappedSuperclass
56 @Audited
57 public abstract class TermBase extends IdentifiableEntity<IIdentifiableEntityCacheStrategy >{
58 private static final long serialVersionUID = 1471561531632115822L;
59 @SuppressWarnings("unused")
60 private static final Logger logger = Logger.getLogger(TermBase.class);
61
62 @XmlElement(name = "URI")
63 @Field(analyze = Analyze.NO)
64 @Type(type="uriUserType")
65 private URI uri;
66
67 /**
68 * The {@link TermType type} of this term. Needs to be the same type in a {@link DefinedTermBase defined term}
69 * and in it's {@link TermVocabulary vocabulary}.
70 */
71 @XmlAttribute(name ="TermType")
72 @Column(name="termType")
73 @NotNull
74 @Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
75 parameters = {@org.hibernate.annotations.Parameter(name = "enumClass", value = "eu.etaxonomy.cdm.model.common.TermType")}
76 )
77 @Audited
78 private TermType termType;
79
80 @XmlElementWrapper(name = "Representations")
81 @XmlElement(name = "Representation")
82 @OneToMany(fetch=FetchType.EAGER, orphanRemoval=true)
83 @Cascade( { CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
84 // @IndexedEmbedded no need for embedding since we are using the DefinedTermBaseClassBridge
85 private Set<Representation> representations = new HashSet<Representation>();
86
87 //******************* CONSTRUCTOR *************************************/
88
89 //for JAXB only, TODO needed?
90 @Deprecated
91 protected TermBase(){}
92
93 protected TermBase(TermType type){
94 super();
95 if (type == null){
96 throw new IllegalArgumentException("TermType must not be null");
97 }else{
98 this.termType = type;
99 }
100 initCacheStrategy();
101 }
102
103 protected TermBase(TermType type, String term, String label, String labelAbbrev) {
104 this(type);
105 this.addRepresentation(new Representation(term, label, labelAbbrev, Language.DEFAULT()) );
106 }
107
108 private void initCacheStrategy() {
109 this.cacheStrategy = new TermDefaultCacheStrategy<TermBase>();
110 }
111
112 //******************** GETTER /SETTER ********************************/
113
114 public TermType getTermType() {
115 return termType;
116 }
117 public void setTermType(TermType termType) {
118 this.termType = termType;
119 }
120
121
122 public Set<Representation> getRepresentations() {
123 return this.representations;
124 }
125
126 public void addRepresentation(Representation representation) {
127 // this.representations.add(representation);
128 this.addToSetWithChangeEvent(this.representations, representation, "representations");
129 }
130
131 public void removeRepresentation(Representation representation) {
132 // this.representations.remove(representation);
133 this.removeFromSetWithChangeEvent(this.representations, representation, "representations");
134 }
135
136 public Representation getRepresentation(Language lang) {
137 for (Representation repr : representations){
138 Language reprLanguage = repr.getLanguage();
139 if (reprLanguage != null && reprLanguage.equals(lang)){
140 return repr;
141 }
142 }
143 return null;
144 }
145
146 /**
147 * @see #getPreferredRepresentation(Language)
148 * @param language
149 * @return
150 */
151 public Representation getPreferredRepresentation(Language language) {
152 Representation repr = getRepresentation(language);
153 if(repr == null){
154 repr = getRepresentation(Language.DEFAULT());
155 }
156 if(repr == null){
157 repr = getRepresentations().isEmpty() ? null : getRepresentations().iterator().next();
158 }
159 return repr;
160 }
161
162 /**
163 * Returns the Representation in the preferred language. Preferred languages
164 * are specified by the parameter languages, which receives a list of
165 * Language instances in the order of preference. If no representation in
166 * any preferred languages is found the method falls back to return the
167 * Representation in Language.DEFAULT() and if necessary further falls back
168 * to return the first element found if any.
169 *
170 * TODO think about this fall-back strategy &
171 * see also {@link TextData#getPreferredLanguageString(List)}
172 *
173 * @param languages
174 * @return
175 */
176 public Representation getPreferredRepresentation(List<Language> languages) {
177 Representation repr = null;
178 if(languages != null){
179 for(Language language : languages) {
180 repr = getRepresentation(language);
181 if(repr != null){
182 return repr;
183 }
184 }
185 }
186 if(repr == null){
187 repr = getRepresentation(Language.DEFAULT());
188 }
189 if(repr == null){
190 Iterator<Representation> it = getRepresentations().iterator();
191 if(it.hasNext()){
192 repr = getRepresentations().iterator().next();
193 }
194 }
195 return repr;
196 }
197
198 public URI getUri() {
199 return this.uri;
200 }
201
202 public void setUri(URI uri) {
203 this.uri = uri;
204 }
205
206 @Transient
207 public String getLabel() {
208 if(getLabel(Language.DEFAULT())!=null){
209 Representation repr = getRepresentation(Language.DEFAULT());
210 return (repr == null)? null :repr.getLabel();
211 }else{
212 for (Representation r : representations){
213 if (r.getLabel()!= null){
214 return r.getLabel();
215 }
216 }
217 if (representations.size()>0){
218 return null;
219 }else{
220 return super.getUuid().toString();
221 }
222 }
223 }
224
225 public String getLabel(Language lang) {
226 Representation repr = this.getRepresentation(lang);
227 return (repr == null) ? null : repr.getLabel();
228 }
229
230 public void setLabel(String label){
231 Language lang = Language.DEFAULT();
232 setLabel(label, lang);
233 }
234
235 public void setLabel(String label, Language language){
236 if (language != null){
237 Representation repr = getRepresentation(language);
238 if (repr != null){
239 repr.setLabel(label);
240 this.titleCache = null; //force titleCache refresh
241 }else{
242 repr = Representation.NewInstance(null, label, null, language);
243 this.addRepresentation(repr);
244 }
245 }
246 }
247
248 @Transient
249 public String getDescription() {
250 return this.getDescription(Language.DEFAULT());
251 }
252
253 public String getDescription(Language lang) {
254 Representation repr = this.getRepresentation(lang);
255 return (repr == null) ? null :repr.getDescription();
256 }
257
258 @Override
259 public boolean equals(Object obj) {
260 if (obj == null){
261 return false;
262 }
263 if (TermBase.class.isAssignableFrom(obj.getClass())){
264 TermBase dtb = (TermBase)obj;
265 if (dtb.getUuid().equals(this.getUuid())){
266 return true;
267 }
268 }
269 return false;
270 }
271
272 @Override
273 public String toString() {
274 //TODO eliminate nasty LazyInitializationException loggings
275 try {
276 return super.toString();
277 } catch (LazyInitializationException e) {
278 return super.toString()+" "+this.getUuid();
279 }
280 }
281
282 //*********************** CLONE ********************************************************/
283
284 /**
285 * Clones <i>this</i> TermBase. This is a shortcut that enables to create
286 * a new instance that differs only slightly from <i>this</i> TermBase by
287 * modifying only some of the attributes.
288 *
289 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
290 * @see java.lang.Object#clone()
291 */
292 @Override
293 public Object clone()throws CloneNotSupportedException {
294
295 TermBase result = (TermBase) super.clone();
296
297 result.representations = new HashSet<Representation>();
298 for (Representation rep : this.representations){
299 result.representations.add((Representation)rep.clone());
300 }
301
302 return result;
303 }
304 }