Project

General

Profile

Download (17 KB) Statistics
| Branch: | Tag: | Revision:
1
package eu.etaxonomy.cdm.model.media;
2

    
3
import java.util.ArrayList;
4
import java.util.HashMap;
5
import java.util.LinkedHashMap;
6
import java.util.LinkedHashSet;
7
import java.util.List;
8
import java.util.Map;
9
import java.util.NoSuchElementException;
10
import java.util.Set;
11
import java.util.SortedMap;
12
import java.util.TreeMap;
13
import java.util.regex.Pattern;
14

    
15
import org.apache.log4j.Logger;
16

    
17
import eu.etaxonomy.cdm.model.common.CdmBase;
18

    
19
public class MediaUtils {
20

    
21
    private static final Logger logger = Logger.getLogger(MediaUtils.class);
22

    
23
    /**
24
     * @param representationPartType TODO
25
     * @param size
26
     * @param height
27
     * @param widthOrDuration
28
     * @param mimeTypeRegexes
29
     * @return
30
     *
31
     *
32
     */
33
    public static MediaRepresentation findBestMatchingRepresentation(Media media,
34
            Class<? extends MediaRepresentationPart> representationPartType, Integer size, Integer height,
35
            Integer widthOrDuration, String[] mimeTypes){
36
        // find best matching representations of each media
37
        SortedMap<Integer, MediaRepresentation> prefRepresentations
38
                = filterAndOrderMediaRepresentations(media.getRepresentations(), null, mimeTypes,
39
                        size, widthOrDuration, height, false);
40
        try {
41
            // take first one and remove all other representations
42
            MediaRepresentation prefOne = prefRepresentations.get(prefRepresentations.firstKey());
43

    
44
            return prefOne;
45

    
46
        } catch (NoSuchElementException nse) {
47
            /* IGNORE */
48
        }
49
        return null;
50
    }
51

    
52
    /**
53
     * Return the first {@link MediaRepresentationPart} found for the given {@link Media}
54
     * or <code>null</code> otherwise.
55
     * @param media the media which is searched for the first part
56
     * @return the first part found or <code>null</code>
57
     */
58
    public static MediaRepresentationPart getFirstMediaRepresentationPart(Media media){
59
        if(media==null){
60
            return null;
61
        }
62
        MediaRepresentationPart mediaRepresentationPart = null;
63
        Set<MediaRepresentation> representations = media.getRepresentations();
64
        if(representations!=null && representations.size()>0){
65
            MediaRepresentation mediaRepresentation = representations.iterator().next();
66
            List<MediaRepresentationPart> parts = mediaRepresentation.getParts();
67
            if(parts!=null && parts.size()>0){
68
                mediaRepresentationPart = parts.iterator().next();
69
            }
70
        }
71
        return mediaRepresentationPart;
72
    }
73

    
74
    /**
75
     * Creates one single {@link MediaRepresentationPart} for the given {@link Media}
76
     * if it does not already exists. Otherwise the first part found is returned.<br>
77
     * @param media the media for which the representation part should be created
78
     * @return the first or newly created representation part
79
     */
80
    public static MediaRepresentationPart initFirstMediaRepresentationPart(Media media, boolean isImage) {
81
        MediaRepresentationPart mediaRepresentationPart = getFirstMediaRepresentationPart(media);
82
        if(mediaRepresentationPart==null){
83
            Set<MediaRepresentation> representations = media.getRepresentations();
84
            if(representations!=null && representations.size()>0){
85
                MediaRepresentation mediaRepresentation = representations.iterator().next();
86
                if(isImage){
87
                    mediaRepresentationPart = ImageFile.NewInstance(null, null);
88
                }
89
                else{
90
                    mediaRepresentationPart = MediaRepresentationPart.NewInstance(null, null);
91
                }
92
                mediaRepresentation.addRepresentationPart(mediaRepresentationPart);
93
            }
94
            else{
95
                if(isImage){
96
                    mediaRepresentationPart = ImageFile.NewInstance(null, null);
97
                }
98
                else{
99
                    mediaRepresentationPart = MediaRepresentationPart.NewInstance(null, null);
100
                }
101

    
102
                MediaRepresentation mediaRepresentation = MediaRepresentation.NewInstance();
103
                mediaRepresentation.addRepresentationPart(mediaRepresentationPart);
104
                media.addRepresentation(mediaRepresentation);
105
            }
106
        }
107
        return mediaRepresentationPart;
108
    }
109

    
110
    /**
111
     * @see #findPreferredMedia(List, Class, String[], String[], Integer, Integer, Integer, boolean)
112
     */
113
    public static Map<Media, MediaRepresentation> findPreferredMedia(List<Media> mediaList,
114
            Class<? extends MediaRepresentationPart> representationPartType, String[] mimeTypes, String[] sizeTokens,
115
            Integer widthOrDuration, Integer height, Integer size
116
            ) {
117
        return findPreferredMedia(mediaList, representationPartType, mimeTypes, sizeTokens,
118
                widthOrDuration, height, size, false);
119
    }
120

    
121

    
122
    /**
123
     * Filters the given List of Media by the supplied filter parameters <code>representationPartType</code>,
124
     * <code>mimeTypes</code>, <code>sizeTokens</code>, <code>widthOrDuration</code>, <code>height</code>, <code>size</code>.
125
     * Only best matching MediaRepresentation remains attached to the Media entities.
126
     * A Media entity may be completely omitted in the resulting list if  {@link #filterAndOrderMediaRepresentations(Set, Class, String[], Integer, Integer, Integer)}
127
     * is not returning any matching representation. This can be the case if a <code>representationPartType</code> is supplied.
128
     * <p>
129
     * In order to prevent the media entities returned by this method from being persisted accidentally the resulting list contains cloned versions of the originally
130
     * supplied media entities, which have the same UUIDs as the original ones.
131
     *
132
     * @param mediaList
133
     * @param representationPartType any subclass of {@link MediaRepresentationPart}
134
     * @param mimeTypes
135
     * @param sizeTokens
136
     * @param widthOrDuration
137
     * @param height
138
     * @param size
139
     * @param onlyBestMatchIfFilterMatches if <code>true</code> and all required filter parameters
140
     *      (like size, width and height) are not <code>null</code> only the best matching representation is returned.
141
     *       Otherwise all representations are returned in sorted order.
142
     * @return
143
     */
144
    public static Map<Media, MediaRepresentation> findPreferredMedia(List<Media> mediaList,
145
            Class<? extends MediaRepresentationPart> representationPartType, String[] mimeTypes, String[] sizeTokens,
146
            Integer widthOrDuration, Integer height, Integer size,
147
            boolean onlyBestMatchIfFilterMatches) {
148

    
149
        if(mimeTypes != null) {
150
            for(int i=0; i<mimeTypes.length; i++){
151
                mimeTypes[i] = mimeTypes[i].replace(':', '/');
152
            }
153
        }
154

    
155
        if(sizeTokens != null) {
156
            if(sizeTokens.length > 0){
157
                try {
158
                    size = Integer.valueOf(sizeTokens[0]);
159
                } catch (NumberFormatException nfe) {
160
                    /* IGNORE */
161
                }
162
            }
163
            if(sizeTokens.length > 1){
164
                try {
165
                    widthOrDuration = Integer.valueOf(sizeTokens[1]);
166
                } catch (NumberFormatException nfe) {
167
                    /* IGNORE */
168
                }
169
            }
170
            if(sizeTokens.length > 2){
171
                try {
172
                    height = Integer.valueOf(sizeTokens[2]);
173
                } catch (NumberFormatException nfe) {
174
                    /* IGNORE */
175
                }
176
            }
177
        }
178

    
179
        Map<Media, MediaRepresentation> returnMediaList;
180
        if(mediaList != null){
181
            returnMediaList = new LinkedHashMap<>(mediaList.size());
182
            for(Media media : mediaList){
183

    
184
                Set<MediaRepresentation> candidateRepresentations = new LinkedHashSet<>();
185
                candidateRepresentations.addAll(media.getRepresentations());
186

    
187
                SortedMap<Integer, MediaRepresentation> prefRepresentations
188
                    = filterAndOrderMediaRepresentations(candidateRepresentations, representationPartType,
189
                            mimeTypes, size, widthOrDuration, height, false);
190
                try {
191
                    if(prefRepresentations.size() > 0){
192
                        // Media.representations is a set
193
                        // so it cannot retain the sorting which has been found by filterAndOrderMediaRepresentations()
194
                        // thus we take first one and remove all other representations
195
                        returnMediaList.put(media, prefRepresentations.get(prefRepresentations.firstKey()));
196
                    }
197
                } catch (NoSuchElementException nse) {
198
                    logger.debug(nse);
199
                    /* IGNORE */
200
                }
201
            }
202
        }
203
        else{
204
            returnMediaList = new HashMap<>();
205
        }
206
        return returnMediaList;
207
    }
208

    
209
    /**
210
     * @param media
211
     * @param mimeTypeRegexes
212
     * @param size
213
     * @param widthOrDuration
214
     * @param height
215
     * @return
216
     *
217
     * TODO move into a media utils class
218
     * TODO implement the quality filter
219

    
220
    public static SortedMap<String, MediaRepresentation> orderMediaRepresentations(Media media, String[] mimeTypeRegexes,
221
            Integer size, Integer widthOrDuration, Integer height) {
222
        SortedMap<String, MediaRepresentation> prefRepr = new TreeMap<String, MediaRepresentation>();
223
        for (String mimeTypeRegex : mimeTypeRegexes) {
224
            // getRepresentationByMimeType
225
            Pattern mimeTypePattern = Pattern.compile(mimeTypeRegex);
226
            int representationCnt = 0;
227
            for (MediaRepresentation representation : media.getRepresentations()) {
228
                int dwa = 0;
229
                if(representation.getMimeType() == null){
230
                    prefRepr.put((dwa + representationCnt++) + "_NA", representation);
231
                } else {
232
                    Matcher mather = mimeTypePattern.matcher(representation.getMimeType());
233
                    if (mather.matches()) {
234

    
235
                        /* TODO the quality filter part is being skipped
236
                         * // look for representation with the best matching parts
237
                        for (MediaRepresentationPart part : representation.getParts()) {
238
                            if (part instanceof ImageFile) {
239
                                ImageFile image = (ImageFile) part;
240
                                int dw = image.getWidth() * image.getHeight() - height * widthOrDuration;
241
                                if (dw < 0) {
242
                                    dw *= -1;
243
                                }
244
                                dwa += dw;
245
                            }
246
                            dwa = (representation.getParts().size() > 0 ? dwa / representation.getParts().size() : 0);
247
                        }
248
                        prefRepr.put((dwa + representationCnt++) + '_' + representation.getMimeType(), representation);
249

    
250
                        // preferred mime type found => end loop
251
                        break;
252
                    }
253
                }
254
            }
255
        }
256
        return prefRepr;
257
    }
258

    
259
    */
260

    
261
    /**
262
     * @param mediaRepresentations
263
     * @param representationPartType
264
     * @param mimeTypeRegexes
265
     * @param size
266
     * @param widthOrDuration
267
     * @param height
268
     * @param onlyBestMatchIfFilterMatches if <code>true</code> and all required filter parameters
269
     *      (like size, width and height) are not <code>null</code> only the best matching representation is returned.
270
     *       Otherwise all representations are returned in sorted order.
271
     * @return
272
     */
273
    private static SortedMap<Integer, MediaRepresentation> filterAndOrderMediaRepresentations(
274
            Set<MediaRepresentation> mediaRepresentations,
275
            Class<? extends MediaRepresentationPart> representationPartType, String[] mimeTypeRegexes,
276
            Integer size, Integer widthOrDuration, Integer height, boolean onlyBestMatchIfFilterMatches) {
277

    
278
        SortedMap<Integer, MediaRepresentation> prefRepr = new TreeMap<>();
279

    
280

    
281
        size = (size == null ? new Integer(0) : size );
282
        widthOrDuration = (widthOrDuration == null ? new Integer(0) : widthOrDuration);
283
        height = (height == null ? new Integer(0) : height);
284
        mimeTypeRegexes = (mimeTypeRegexes == null ? new String[]{".*"} : mimeTypeRegexes);
285

    
286
        boolean filterMatches = true;
287
        for (String mimeTypeRegex : mimeTypeRegexes) {
288
            // getRepresentationByMimeType
289
            Pattern mimeTypePattern = Pattern.compile(mimeTypeRegex);
290
            int representationCnt = 0;
291
            for (MediaRepresentation representation : mediaRepresentations) {
292

    
293
                List<MediaRepresentationPart> matchingParts = new ArrayList<>();
294

    
295

    
296
                // check MIME type
297
                boolean mimeTypeOK = representation.getMimeType() == null
298
                        || mimeTypePattern.matcher(representation.getMimeType()).matches();
299
                logger.debug("mimeTypeOK: " + Boolean.valueOf(mimeTypeOK).toString());
300

    
301
                int dwa = 0;
302

    
303
                //first the size is used for comparison
304
                for (MediaRepresentationPart part : representation.getParts()) {
305

    
306
                    // check representationPartType
307
                    boolean representationPartTypeOK = representationPartType == null
308
                            || part.getClass().isAssignableFrom(representationPartType);
309
                    logger.debug("representationPartTypeOK: " + Boolean.valueOf(representationPartTypeOK).toString());
310

    
311
                    if ( !(representationPartTypeOK && mimeTypeOK) ) {
312
                        continue;
313
                    }
314

    
315
                    logger.debug(part + " matches");
316
                    matchingParts.add(part);
317

    
318
                    boolean partFilterMatches = false;
319

    
320
                    if (part.getSize()!= null){
321
                        int sizeOfPart = part.getSize();
322
                        int distance = sizeOfPart - size;
323
                        if (distance < 0) {
324
                            distance *= -1;
325
                        }
326
                        dwa += distance;
327
                        if (size > 0){
328
                            partFilterMatches = true;
329
                        }
330
                    }
331

    
332
                    //if height and width/duration is defined, add this information, too
333
                    if (height != 0 && widthOrDuration != 0){
334
                        int durationWidthWeight = 0;
335

    
336
                        if (part.isInstanceOf(ImageFile.class)) {
337
                            ImageFile image = CdmBase.deproxy(part, ImageFile.class);
338
                            if (image.getWidth() != null && image.getHeight() != null){
339
                                durationWidthWeight = image.getWidth() * image.getHeight() - height * widthOrDuration;
340
                            }
341
                        }
342
                        else if (part.isInstanceOf(MovieFile.class)){
343
                            MovieFile movie = CdmBase.deproxy(part, MovieFile.class);
344
                            durationWidthWeight = movie.getDuration() - widthOrDuration;
345
                        }else if (part.isInstanceOf(AudioFile.class)){
346
                            AudioFile audio = CdmBase.deproxy(part, AudioFile.class);
347
                            durationWidthWeight = audio.getDuration() - widthOrDuration;
348
                        }
349
                        if (durationWidthWeight < 0) {
350
                            durationWidthWeight *= -1;
351
                        }
352
                        dwa += durationWidthWeight;
353

    
354
                    }else{
355
                        partFilterMatches = true;
356
                    }
357
                    filterMatches &= partFilterMatches;
358
                } // loop parts
359
                logger.debug("matchingParts.size():" + matchingParts.size());
360
                if(matchingParts.size() > 0 ){
361
                    dwa = dwa / matchingParts.size();
362

    
363
                    representation.getParts().clear();
364
                    representation.getParts().addAll(matchingParts);
365

    
366
                    //keyString =(dwa + representationCnt++) + '_' + representation.getMimeType();
367

    
368
                    prefRepr.put((dwa + representationCnt++), representation);
369
                }
370
            } // loop representations
371
        } // loop mime types
372
        logger.debug(prefRepr.size() + " preferred representations found");
373

    
374
        if (onlyBestMatchIfFilterMatches && filterMatches){
375
            SortedMap<Integer, MediaRepresentation> result = new TreeMap<>();
376
            try {
377
                result.put(prefRepr.firstKey(), prefRepr.get(prefRepr.firstKey()));
378
                return result;
379
            } catch (Exception e) {
380
                return result;
381
            }
382
        }else{
383
            return prefRepr;
384
        }
385
    }
386
}
(9-9/13)