Project

General

Profile

Download (14.2 KB) Statistics
| Branch: | Tag: | Revision:
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.media;
11

    
12
import java.util.HashMap;
13
import java.util.HashSet;
14
import java.util.Map;
15
import java.util.Set;
16

    
17
import javax.persistence.Embedded;
18
import javax.persistence.Entity;
19
import javax.persistence.FetchType;
20
import javax.persistence.Inheritance;
21
import javax.persistence.InheritanceType;
22
import javax.persistence.JoinTable;
23
import javax.persistence.ManyToOne;
24
import javax.persistence.MapKeyJoinColumn;
25
import javax.persistence.OneToMany;
26
import javax.persistence.Transient;
27
import javax.validation.constraints.NotEmpty;
28
import javax.validation.constraints.NotNull;
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.XmlType;
37
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
38

    
39
import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
40
import org.hibernate.annotations.Cascade;
41
import org.hibernate.annotations.CascadeType;
42
import org.hibernate.envers.Audited;
43
import org.hibernate.search.annotations.IndexedEmbedded;
44

    
45
import eu.etaxonomy.cdm.common.URI;
46
import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
47
import eu.etaxonomy.cdm.model.agent.AgentBase;
48
import eu.etaxonomy.cdm.model.common.IIntextReferenceTarget;
49
import eu.etaxonomy.cdm.model.common.IMultiLanguageTextHolder;
50
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
51
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
52
import eu.etaxonomy.cdm.model.common.Language;
53
import eu.etaxonomy.cdm.model.common.LanguageString;
54
import eu.etaxonomy.cdm.model.common.MultilanguageText;
55
import eu.etaxonomy.cdm.model.common.TimePeriod;
56
import eu.etaxonomy.cdm.model.reference.Reference;
57
import eu.etaxonomy.cdm.strategy.cache.media.IMediaCacheStrategy;
58
import eu.etaxonomy.cdm.strategy.cache.media.MediaDefaultCacheStrategy;
59
import eu.etaxonomy.cdm.validation.Level2;
60

    
61
/**
62
 * A {@link Media media} is any kind of media that represents a media object.
63
 * This media object can have multiple {@link MediaRepresentation media representations} that differ in MIME-type
64
 * and/or quality.
65
 * E.g.
66
 * (1) an image can have a tiff and a jpg media representation.
67
 * (2) an formatted text can have a text/html or an application/pdf representation.
68
 *
69
 * @author m.doering
70
 * @since 08-Nov-2007 13:06:34
71
 */
72
@XmlAccessorType(XmlAccessType.FIELD)
73
@XmlType(name = "Media", propOrder = {
74
    "title",
75
    "mediaCreated",
76
    "description",
77
    "representations",
78
    "artist",
79
    "link"
80
})
81
@XmlRootElement(name = "Media")
82
@Entity
83
//@Indexed disabled to reduce clutter in indexes, since this type is not used by any search
84
//@Indexed
85
@Audited
86
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
87
public class Media
88
        extends IdentifiableEntity<IMediaCacheStrategy>
89
        implements IMultiLanguageTextHolder, IIntextReferenceTarget {
90

    
91
    private static final long serialVersionUID = -1927421567263473658L;
92
    @SuppressWarnings("unused")
93
    private static final Logger logger = LogManager.getLogger(Media.class);
94

    
95
    // TODO once hibernate annotations support custom collection type
96
    // private MultilanguageText title = new MultilanguageText();
97
    @XmlElement(name = "MediaTitle")
98
    @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
99
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
100
    @MapKeyJoinColumn(name="title_mapkey_id")
101
    @IndexedEmbedded
102
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE, CascadeType.REFRESH})
103
    @NotNull
104
    @NotEmpty(groups = Level2.class)
105
    private Map<Language,LanguageString> title = new HashMap<>();
106

    
107
    //creation date of the media (not of the record)
108
    @XmlElement(name ="MediaCreated" )
109
    @Embedded
110
    @IndexedEmbedded
111
    private TimePeriod mediaCreated;
112

    
113
    // TODO once hibernate annotations support custom collection type
114
    // private MultilanguageText description = new MultilanguageText();
115
    @XmlElement(name = "MediaDescription")
116
    @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
117
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
118
    @MapKeyJoinColumn(name="description_mapkey_id")
119
    @JoinTable(name = "Media_Description")
120
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE,CascadeType.DELETE, CascadeType.REFRESH})
121
    @IndexedEmbedded
122
    @NotNull
123
    private Map<Language,LanguageString> description = new HashMap<>();
124

    
125
    //A single medium such as a picture can have multiple representations in files.
126
    //Common are multiple resolutions or file formats for images for example
127
    @XmlElementWrapper(name = "MediaRepresentations")
128
    @XmlElement(name = "MediaRepresentation")
129
    @OneToMany(mappedBy="media",fetch = FetchType.LAZY, orphanRemoval=true)
130
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.REFRESH})
131
    @NotNull
132
    @NotEmpty(groups = Level2.class)
133
    private Set<MediaRepresentation> representations = new HashSet<>();
134

    
135
    @XmlElement(name = "Artist")
136
    @XmlIDREF
137
    @XmlSchemaType(name = "IDREF")
138
    @ManyToOne(fetch = FetchType.LAZY)
139
    @IndexedEmbedded
140
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
141
    private AgentBase<?> artist;
142

    
143
    @XmlElement(name = "Link")
144
    @XmlIDREF
145
    @XmlSchemaType(name = "IDREF")
146
    @ManyToOne(fetch = FetchType.LAZY)
147
    @IndexedEmbedded
148
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE})
149
    private ExternalLink link;
150

    
151
//************************* FACTORY METHODS *******************************/
152

    
153
    public static Media NewInstance(){
154
        return new Media();
155
    }
156

    
157
    /**
158
     * Factory method which creates a new media, adds a reprsentation including mime type and suffix information
159
     * and adds to the later a representation part for a given uri and size
160
     * Returns <code>null</code> if uri is empty
161
     * @return Media
162
     */
163
    public static Media NewInstance(URI uri, Integer size, String mimeType, String suffix){
164
    	//TODO improve type recognition
165
    	Class<? extends MediaRepresentationPart> clazz = null;
166
    	if (isNotBlank(mimeType)){
167
    		if (mimeType.matches("image.*")){
168
    			clazz = ImageFile.class;
169
    		}
170
    	}
171
    	if (isNotBlank(suffix)){
172
    		if (suffix.matches("\\.(gif|jpe?g|tiff?)")){
173
    			clazz = ImageFile.class;
174
    		}
175
    	}else if (uri != null){
176
    		if (uri.toString().matches("\\.(gif|jpe?g|tiff?)")){
177
    			clazz = ImageFile.class;
178
    		}
179
    	}
180
    	MediaRepresentation representation = MediaRepresentation.NewInstance(mimeType, suffix, uri, size,clazz);
181
        if (representation == null){
182
            return null;
183
        }
184
        Media media = new Media();
185
        media.addRepresentation(representation);
186
        return media;
187
    }
188

    
189
//********************************* CONSTRUCTOR **************************/
190

    
191
    protected Media() {
192
        super();
193
    }
194

    
195
    @Override
196
    protected void initDefaultCacheStrategy() {
197
        //if (getClass() == Media.class) in future we may distinguish cache strategies for subclasses
198
        this.cacheStrategy = MediaDefaultCacheStrategy.NewInstance();
199
    }
200

    
201
// ********************* GETTER / SETTER    **************************/
202

    
203
    public Set<MediaRepresentation> getRepresentations(){
204
        if(representations == null) {
205
            this.representations = new HashSet<>();
206
        }
207
        return this.representations;
208
    }
209

    
210
    @SuppressWarnings("deprecation")
211
    public void addRepresentation(MediaRepresentation representation){
212
        if (representation != null){
213
            this.getRepresentations().add(representation);
214
            representation.setMedia(this);
215
        }
216
    }
217

    
218
    @SuppressWarnings("deprecation")
219
    public void removeRepresentation(MediaRepresentation representation){
220
        this.getRepresentations().remove(representation);
221
        if (representation != null){
222
            representation.setMedia(null);
223
        }
224
    }
225

    
226
    public AgentBase<?> getArtist(){
227
        return this.artist;
228
    }
229
    public void setArtist(AgentBase<?> artist){
230
        this.artist = artist;
231
    }
232

    
233
    public ExternalLink getLink() {
234
        return link;
235
    }
236
    public void setLink(ExternalLink link) {
237
        this.link = link;
238
    }
239

    
240
//************************ title / title cache *********************************
241

    
242
    /**
243
     * Returns the title in the default language.
244
     */
245
    public LanguageString getTitle(){
246
        return getTitle(Language.DEFAULT());
247
    }
248

    
249
    public LanguageString getTitle(Language language){
250
        return title.get(language);
251
    }
252

    
253
    @Transient
254
    public Map<Language,LanguageString> getAllTitles(){
255
        return this.title;
256
    }
257
    /**
258
     * Adds the languageString to the {@link MultilanguageText multilanguage text}
259
     * used to be the title of <i>this</i> media.
260
     *
261
     * @param title		the languageString with the title in a particular language
262
     * @see    	   		#getTitle()
263
     * @see    	   		#putTitle(Language String)
264
    */
265
    public void putTitle(LanguageString title){
266
        LanguageString oldValue = this.title.put(title.getLanguage(), title);
267
        resetTitleCache();  //TODO #9632 handle with aspectj
268
        this.firePropertyChange("title", oldValue, title);  //this is not fully correct but it is a workaround anyway
269

    
270
    }
271

    
272
    /**
273
     * Creates a {@link LanguageString language string} based on the given text string
274
     * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
275
     * used to be the title of <i>this</i> media.
276
     *
277
     * @param language	the language in which the title string is formulated
278
     * @param text		the title in a particular language
279
     *
280
     * @see    	   		#getTitle()
281
     * @see    	   		#putTitle(LanguageString)
282
     */
283
    public void putTitle(Language language, String title){
284
        putTitle(LanguageString.NewInstance(title, language));
285
    }
286

    
287
    public void removeTitle(Language language){
288
        LanguageString oldValue = this.title.remove(language);
289
        resetTitleCache(); //TODO #9632 handle with aspectj
290
        this.firePropertyChange("title", oldValue, null);  //this is not fully correct but it is a workaround anyway
291
    }
292

    
293
    @Transient
294
    public String getTitleCacheByLanguage(Language lang){
295
        if (cacheStrategy() != null){
296
            return ((MediaDefaultCacheStrategy)cacheStrategy()).getTitleCacheByLanguage(this, lang);
297
        }else{
298
            return null;
299
        }
300
    }
301

    
302
    /**
303
     * Puts the title into the title field which is a multi-language string
304
     * with default language as language.
305
     * TODO: this may be adapted in future as it overrides the titleCache setter, not sure why it was implemented like this
306
     */
307
    @Override
308
    public void setTitleCache(String titleCache) {
309
        putTitle(LanguageString.NewInstance(titleCache, Language.DEFAULT()));
310
    }
311

    
312
    @Override
313
    public String getTitleCache(){
314
        if (protectedTitleCache){
315
            return this.titleCache;
316
        }
317
        // is title dirty, i.e. equal NULL?
318
        if (titleCache == null){
319
            this.titleCache = generateTitle();
320
            this.titleCache = getTruncatedCache(this.titleCache) ;
321
        }else{
322
            //do the same as listeners on dependend objects like representations parts
323
            //are not yet installed
324
            this.titleCache = generateTitle();
325
            this.titleCache = getTruncatedCache(this.titleCache) ;
326
        }
327
        return titleCache;
328
    }
329

    
330
    public TimePeriod getMediaCreated(){
331
        return this.mediaCreated;
332
    }
333

    
334
    public void setMediaCreated(TimePeriod mediaCreated){
335
        this.mediaCreated = mediaCreated;
336
    }
337

    
338
    //************* Descriptions  *********************/
339

    
340
    public Map<Language,LanguageString> getAllDescriptions(){
341
        if(this.description == null) {
342
            this.description = new HashMap<>();
343
        }
344
        return this.description;
345
    }
346

    
347
    public LanguageString getDescription(Language language){
348
        return getAllDescriptions().get(language);
349
    }
350

    
351
    public void putDescription(LanguageString description){
352
        LanguageString oldValue = this.description.put(description.getLanguage(), description);
353
        resetTitleCache(); //TODO #9632 handle with aspectj
354
        this.firePropertyChange("title", oldValue, description);  //this is not fully correct but it is a workaround anyway
355
    }
356

    
357
    public void putDescription(Language language, String text){
358
        putDescription(LanguageString.NewInstance(text, language));
359
    }
360

    
361
    public void removeDescription(Language language){
362
        LanguageString oldValue = this.description.remove(language);
363
        resetTitleCache(); //TODO #9632 handle with aspectj
364
        this.firePropertyChange("title", oldValue, null);  //this is not fully correct but it is a workaround anyway
365
   }
366

    
367
// ************************ SOURCE ***************************/
368

    
369
    public IdentifiableSource addPrimaryMediaSource(Reference citation, String microCitation) {
370
        if (citation == null && microCitation == null){
371
            return null;
372
        }
373
        IdentifiableSource source = IdentifiableSource.NewPrimaryMediaSourceInstance(citation, microCitation);
374
        addSource(source);
375
        return source;
376
    }
377

    
378
//************************* CLONE **************************/
379

    
380
    @Override
381
    public Media clone() throws CloneNotSupportedException{
382
        Media result = (Media)super.clone();
383
        //description
384
        result.description = cloneLanguageString(this.description);
385

    
386
        //title
387
        result.title = cloneLanguageString(this.title);
388

    
389
        //media representations
390
        result.representations = new HashSet<>();
391
        for (MediaRepresentation mediaRepresentation: this.representations){
392
            result.representations.add(mediaRepresentation.clone());
393
        }
394

    
395
        result.link = this.link != null ? this.link.clone(): null;
396

    
397
        //no changes to: artist
398
        return result;
399
    }
400

    
401
    public int compareTo(Object o) {
402
        return 0;
403
    }
404

    
405

    
406

    
407
}
(8-8/16)