minor
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / description / TextData.java
1 /**
2 * Copyright (C) 2007 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.description;
11
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.List;
15 import java.util.Map;
16
17 import javax.persistence.Entity;
18 import javax.persistence.FetchType;
19 import javax.persistence.ManyToOne;
20 import javax.persistence.OneToMany;
21 import javax.persistence.Transient;
22 import javax.validation.constraints.NotNull;
23 import javax.xml.bind.annotation.XmlAccessType;
24 import javax.xml.bind.annotation.XmlAccessorType;
25 import javax.xml.bind.annotation.XmlElement;
26 import javax.xml.bind.annotation.XmlIDREF;
27 import javax.xml.bind.annotation.XmlRootElement;
28 import javax.xml.bind.annotation.XmlSchemaType;
29 import javax.xml.bind.annotation.XmlTransient;
30 import javax.xml.bind.annotation.XmlType;
31 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
32
33 import org.apache.log4j.Logger;
34 import org.hibernate.annotations.Cascade;
35 import org.hibernate.annotations.CascadeType;
36 import org.hibernate.envers.Audited;
37 import org.hibernate.search.annotations.Indexed;
38 import org.hibernate.search.annotations.IndexedEmbedded;
39
40 import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
41 import eu.etaxonomy.cdm.model.common.IMultiLanguageTextHolder;
42 import eu.etaxonomy.cdm.model.common.Language;
43 import eu.etaxonomy.cdm.model.common.LanguageString;
44 import eu.etaxonomy.cdm.model.common.MultilanguageTextHelper;
45 import eu.etaxonomy.cdm.model.common.TermBase;
46
47
48 /**
49 * This class represents information pieces expressed in one or several natural
50 * languages (for the {@link Feature feature} "medical use" for instance).
51 * A {@link TextFormat format} used for structuring the text may also be stated.
52 * <P>
53 * This class corresponds partially to NaturalLanguageDescriptionType according
54 * to the SDD schema.
55 *
56 * @author m.doering
57 * @version 1.0
58 * @created 08-Nov-2007 13:06:59
59 */
60 @XmlAccessorType(XmlAccessType.FIELD)
61 @XmlType(name = "TextData", propOrder = {
62 "multilanguageText",
63 "format"
64 })
65 @XmlRootElement(name = "TextData")
66 @Entity
67 @Audited
68 @Indexed(index = "eu.etaxonomy.cdm.model.description.DescriptionElementBase")
69 public class TextData extends DescriptionElementBase implements IMultiLanguageTextHolder, Cloneable{
70 private static final long serialVersionUID = -2165015581278282615L;
71 private static final Logger logger = Logger.getLogger(TextData.class);
72
73 //@XmlElement(name = "MultiLanguageText", type = MultilanguageText.class)
74 @XmlElement(name = "MultiLanguageText")
75 @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
76 @OneToMany (fetch= FetchType.LAZY)
77 @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN })
78 @IndexedEmbedded
79 @NotNull
80 private Map<Language, LanguageString> multilanguageText = new HashMap<Language,LanguageString>();
81
82 @XmlElement(name = "Format")
83 @XmlIDREF
84 @XmlSchemaType(name = "IDREF")
85 @ManyToOne(fetch = FetchType.LAZY)
86 private TextFormat format;
87
88 @XmlTransient
89 @Transient
90 private boolean isHashMapHibernateBugFixed = false;
91
92 // ************* CONSTRUCTORS *************/
93 /**
94 * Class constructor: creates a new empty text data instance.
95 *
96 * @see #TextData(Feature)
97 */
98 public TextData(){
99 this(null);
100 }
101
102 /**
103 * Class constructor: creates a new text data instance with the {@link Feature feature}
104 * to be described.
105 *
106 * @param feature the feature the text data refer to
107 * @see #TextData()
108 */
109 public TextData(Feature feature){
110 super(feature);
111 }
112
113 //********* METHODS **************************************/
114 /**
115 * Creates a new empty text data instance.
116 *
117 * @see #NewInstance(Feature)
118 * @see #NewInstance(String, Language, TextFormat)
119 */
120 public static TextData NewInstance(){
121 return new TextData();
122 }
123
124 /**
125 * Creates a new text data instance with the {@link Feature feature}
126 * to be described.
127 *
128 * @param feature the feature the text data refer to
129 * @see #NewInstance()
130 * @see #NewInstance(String, Language, TextFormat)
131 */
132 public static TextData NewInstance(Feature feature){
133 return new TextData(feature);
134 }
135
136 /**
137 * Creates a new text data instance with a given text in a given particular
138 * {@link Language language} and with the given text format for structuring it.
139 *
140 * @param text the text string with the content of the description
141 * @param language the language in which the text string is formulated
142 * @param format the text format used to structure the text string
143 * @see #NewInstance()
144 * @see #NewInstance(Feature)
145 */
146 public static TextData NewInstance(String text, Language language, TextFormat format){
147 TextData result = new TextData();
148 result.putText(text, language);
149 result.setFormat(format);
150 return result;
151 }
152
153 /**
154 * Returns a copy of the multilanguage text with the content of <i>this</i> text data.
155 * The different {@link LanguageString language strings} (texts) contained in the
156 * multilanguage text should all have the same meaning.
157 *
158 * @see #getText(Language)
159 */
160 public Map<Language, LanguageString> getMultilanguageText() {
161 fixHashMapHibernateBug();
162
163 // HashMap<Language, LanguageString> result = new HashMap<Language, LanguageString>();
164 // result.putAll(multilanguageText);
165 // return result;
166 return multilanguageText;
167 }
168
169 // /**
170 // * Sets the multilanguage text.
171 // * The different {@link LanguageString language strings} (texts) contained in the
172 // * multilanguage text should all have the same meaning.
173 // *
174 // * @param multilanguageText
175 // */
176 // private void setMultilanguageText(Map<Language,LanguageString> multilanguageText) {
177 // this.multilanguageText = multilanguageText;
178 // }
179
180 /**
181 * Returns the multilanguage text with the content of <i>this</i> text data for
182 * a specific language.
183 *
184 * @param language the language in which the text string looked for is formulated
185 * @return
186 */
187 public LanguageString getLanguageText(Language language){
188 //work around for the problem that contains does not work correctly in persisted maps.
189 //This is because the persisted uuid is not present when loading the map key and
190 //therefore the hash code for language is not computed correctly
191 //see DescriptionElementDaoHibernateTest and #2114
192 // for (Map.Entry<Language, LanguageString> entry : multilanguageText.entrySet()){
193 // if (entry.getKey() != null){
194 // if (entry.getKey().equals(language)){
195 // return entry.getValue();
196 // }
197 // }else{
198 // if (language == null){
199 // return entry.getValue();
200 // }
201 // }
202 // }
203 // return null;
204 //old
205 return getMultilanguageText().get(language);
206 }
207
208 /**
209 * Returns the text string in the given {@link Language language} with the content
210 * of <i>this</i> text data.
211 *
212 * @param language the language in which the text string looked for is formulated
213 * @see #getMultilanguageText(Language)
214 */
215 public String getText(Language language) {
216 LanguageString languageString = getLanguageText(language);
217 if (languageString == null){
218 return null;
219 }else{
220 return languageString.getText();
221 }
222 }
223
224 /**
225 * Returns the LanguageString in the preferred language. Preferred languages
226 * are specified by the parameter languages, which receives a list of
227 * Language instances in the order of preference. If no representation in
228 * any preferred languages is found the method falls back to return the
229 * Representation in Language.DEFAULT() and if neccesary further falls back
230 * to return the first element found if any.
231 *
232 * TODO think about this fall-back strategy &
233 * see also {@link TermBase#getPreferredRepresentation(List)}
234 *
235 * @param languages
236 * @return
237 */
238 public LanguageString getPreferredLanguageString(List<Language> languages) {
239 return MultilanguageTextHelper.getPreferredLanguageString(getMultilanguageText(), languages);
240 }
241
242 private void fixHashMapHibernateBug() {
243 //workaround for key problem
244 if(! isHashMapHibernateBugFixed){
245 HashMap<Language, LanguageString> tmp = new HashMap<Language, LanguageString>();
246 tmp.putAll(multilanguageText);
247 multilanguageText.clear();
248 multilanguageText.putAll(tmp);
249
250 isHashMapHibernateBugFixed = true;
251 }
252 }
253
254 /**
255 * Creates a {@link LanguageString language string} based on the given text string
256 * and the given {@link Language language}, returns it and adds it to the multilanguage
257 * text representing the content of <i>this</i> text data.
258 *
259 * @param text the string representing the content of the text data
260 * in a particular language
261 * @param language the language in which the text string is formulated
262 * @see #getMultilanguageText()
263 * @see #putText(LanguageString)
264 * @return the previous language string associated with the given Language, or null if there was no mapping for the given Language
265 * @deprecated should follow the put semantic of maps, this method will be removed in v4.0
266 * Use the {@link #putText(Language, String) putText} method instead
267 */
268 @Deprecated
269 public LanguageString putText(String text, Language language) {
270 return this.putText(language, text);
271 }
272
273 /**
274 * Creates a {@link LanguageString language string} based on the given text string
275 * and the given {@link Language language}, returns it and adds it to the multilanguage
276 * text representing the content of <i>this</i> text data.
277 *
278 * @param language the language in which the text string is formulated
279 * @param text the string representing the content of the text data
280 * in a particular language
281 *
282 * @see #getMultilanguageText()
283 * @see #putText(LanguageString)
284 * @return the previous language string associated with the given Language, or null if there was no mapping for the given Language
285 */
286 public LanguageString putText(Language language, String text) {
287 fixHashMapHibernateBug();
288 //** end workaround
289 LanguageString languageString = multilanguageText.get(language);
290 if (languageString != null){
291 languageString.setText(text);
292 }else{
293 languageString = LanguageString.NewInstance(text, language);
294 }
295 LanguageString result = this.multilanguageText.put(language , languageString);
296 return (result == null ? null : result);
297 }
298
299
300 /**
301 * Adds a translated {@link LanguageString text in a particular language}
302 * to the multi-language text representing the content of <i>this</i> text data.
303 * The given language string will be returned.
304 *
305 * @param languageString the language string representing the content of
306 * the text data in a particular language
307 * @see #getMultilanguageText()
308 * @see #putText(String, Language)
309 * @see HashMap#put(Object, Object)
310 * @return the previous language string associated with key, or null if there was no mapping for key
311 */
312 public LanguageString putText(LanguageString languageString) {
313
314 if (languageString == null){
315 return null;
316 }else{
317 Language language = languageString.getLanguage();
318 return this.multilanguageText.put(language, languageString);
319 }
320 }
321 /**
322 * Removes from the multilanguage representing the content of
323 * <i>this</i> text data the one {@link LanguageString language string}
324 * with the given {@link Language language}. Returns the removed
325 * language string.
326 *
327 * @param language the language in which the language string to be removed
328 * has been formulated
329 * @return the language string associated with the given language or null if there was no mapping for the given Language
330 * @see #getMultilanguageText()
331 */
332 public LanguageString removeText(Language language) {
333 fixHashMapHibernateBug();
334 return this.multilanguageText.remove(language);
335 }
336
337 /**
338 * Returns the number of {@link Language languages} in which the content
339 * of <i>this</i> text data has been formulated.
340 *
341 * @see #getMultilanguageText()
342 */
343 public int countLanguages(){
344 return multilanguageText.size();
345 }
346
347
348 /**
349 * Returns the {@link TextFormat format} used for structuring the text representing
350 * the content of <i>this</i> text data.
351 *
352 * @see #getMultilanguageText()
353 */
354 public TextFormat getFormat() {
355 return format;
356 }
357 /**
358 * @see #getFormat()
359 */
360 public void setFormat(TextFormat format) {
361 this.format = format;
362 }
363
364 /**
365 * @see {@link java.util.Map#containsKey(Object)}
366 * @param language
367 * @return
368 */
369 public boolean containsKey(Language language){
370 return getMultilanguageText().containsKey(language);
371 }
372
373 /**
374 * @see {@link java.util.Map#containsValue(Object)}
375 * @param languageString
376 * @return
377 */
378 public boolean containsValue(LanguageString languageString){
379 return getMultilanguageText().containsValue(languageString);
380 }
381
382
383 /**
384 * Returns the number of languages available for this text data.
385 * @see {@link java.util.Map#size()}
386 * @return
387 */
388 public int size(){
389 return this.multilanguageText.size();
390 }
391
392
393 //*********************************** CLONE *****************************************/
394
395 /**
396 * Clones <i>this</i> text data. This is a shortcut that enables to create
397 * a new instance that differs only slightly from <i>this</i> text data by
398 * modifying only some of the attributes.
399 *
400 * @see eu.etaxonomy.cdm.model.description.DescriptionElementBase#clone()
401 * @see java.lang.Object#clone()
402 */
403 @Override
404 public Object clone() {
405
406 try {
407 TextData result = (TextData)super.clone();
408
409 //description
410 result.multilanguageText = new HashMap<Language, LanguageString>();
411 for (Language language : getMultilanguageText().keySet()){
412 //TODO clone needed? See also IndividualsAssociation
413 LanguageString newLanguageString = (LanguageString)getMultilanguageText().get(language).clone();
414 result.multilanguageText.put(language, newLanguageString);
415 }
416
417 return result;
418 //no changes to: format
419 } catch (CloneNotSupportedException e) {
420 logger.warn("Object does not implement cloneable");
421 e.printStackTrace();
422 return null;
423 }
424 }
425
426 }