1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.opencastproject.mediapackage;
24
25 import org.opencastproject.mediapackage.MediaPackageElement.Type;
26 import org.opencastproject.mediapackage.identifier.Id;
27 import org.opencastproject.mediapackage.identifier.IdImpl;
28 import org.opencastproject.util.DateTimeSupport;
29 import org.opencastproject.util.IoSupport;
30 import org.opencastproject.util.XmlSafeParser;
31
32 import org.apache.commons.io.IOUtils;
33 import org.apache.commons.lang3.StringUtils;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36 import org.w3c.dom.Node;
37 import org.w3c.dom.bootstrap.DOMImplementationRegistry;
38 import org.w3c.dom.ls.DOMImplementationLS;
39 import org.w3c.dom.ls.LSOutput;
40 import org.w3c.dom.ls.LSSerializer;
41 import org.xml.sax.SAXException;
42
43 import java.io.ByteArrayInputStream;
44 import java.io.ByteArrayOutputStream;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.net.URI;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.Collection;
51 import java.util.Date;
52 import java.util.HashSet;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.Set;
56 import java.util.TreeSet;
57
58 import javax.xml.bind.JAXBContext;
59 import javax.xml.bind.JAXBException;
60 import javax.xml.bind.Unmarshaller;
61 import javax.xml.bind.annotation.XmlAccessType;
62 import javax.xml.bind.annotation.XmlAccessorType;
63 import javax.xml.bind.annotation.XmlAttribute;
64 import javax.xml.bind.annotation.XmlElement;
65 import javax.xml.bind.annotation.XmlElementWrapper;
66 import javax.xml.bind.annotation.XmlRootElement;
67 import javax.xml.bind.annotation.XmlType;
68 import javax.xml.bind.annotation.adapters.XmlAdapter;
69 import javax.xml.transform.stream.StreamSource;
70
71
72
73
74 @XmlType(name = "mediapackage", namespace = "http://mediapackage.opencastproject.org", propOrder = { "title", "series",
75 "seriesTitle", "creators", "contributors", "subjects", "license", "language", "tracks", "catalogs",
76 "attachments", "publications" })
77 @XmlRootElement(name = "mediapackage", namespace = "http://mediapackage.opencastproject.org")
78 @XmlAccessorType(XmlAccessType.NONE)
79 public final class MediaPackageImpl implements MediaPackage {
80
81
82 private static final Logger logger = LoggerFactory.getLogger(MediaPackageImpl.class.getName());
83
84
85
86
87
88 public static final String NEGATE_TAG_PREFIX = "-";
89
90
91 static final JAXBContext context;
92
93
94 private MediaPackageElementBuilder mediaPackageElementBuilder = null;
95
96 @XmlElement(name = "title")
97 private String title = null;
98
99 @XmlElement(name = "seriestitle")
100 private String seriesTitle = null;
101
102 @XmlElement(name = "language")
103 private String language = null;
104
105 @XmlElement(name = "series")
106 private String series = null;
107
108 @XmlElement(name = "license")
109 private String license = null;
110
111 @XmlElementWrapper(name = "creators")
112 @XmlElement(name = "creator")
113 private Set<String> creators = null;
114
115 @XmlElementWrapper(name = "contributors")
116 @XmlElement(name = "contributor")
117 private Set<String> contributors = null;
118
119 @XmlElementWrapper(name = "subjects")
120 @XmlElement(name = "subject")
121 private Set<String> subjects = null;
122
123
124 private Id identifier = null;
125
126
127 private long startTime = 0L;
128
129
130 private Long duration = null;
131
132
133 private final List<MediaPackageElement> elements = new ArrayList<MediaPackageElement>();
134
135
136 private int tracks = 0;
137
138
139 private int catalogs = 0;
140
141
142 private int attachments = 0;
143
144
145 private int others = 0;
146
147 static {
148 try {
149 context = JAXBContext.newInstance("org.opencastproject.mediapackage", MediaPackageImpl.class.getClassLoader());
150 } catch (JAXBException e) {
151 throw new RuntimeException(e);
152 }
153 }
154
155
156
157
158 MediaPackageImpl() {
159 this(IdImpl.fromUUID());
160 }
161
162
163
164
165
166
167
168 MediaPackageImpl(Id id) {
169 this.identifier = id;
170 }
171
172
173
174
175
176
177 @XmlAttribute(name = "id")
178 @Override
179 public Id getIdentifier() {
180 return identifier;
181 }
182
183
184
185
186
187
188 @Override
189 public void setIdentifier(Id identifier) {
190 this.identifier = identifier;
191 }
192
193
194
195
196
197
198 @XmlAttribute(name = "duration")
199 @Override
200 public Long getDuration() {
201 if (duration == null && hasTracks()) {
202 recalculateDuration();
203 }
204 return duration;
205 }
206
207
208
209
210 private void recalculateDuration() {
211
212 duration = null;
213 for (Track t : getTracks()) {
214 if (t.getDuration() != null) {
215 if (duration == null || duration < t.getDuration())
216 duration = t.getDuration();
217 }
218 }
219 }
220
221
222
223
224
225
226 @Override
227 public void setDuration(Long duration) throws IllegalStateException {
228 if (hasTracks())
229 throw new IllegalStateException(
230 "The duration is determined by the length of the tracks and cannot be set manually");
231 this.duration = duration;
232 }
233
234
235
236
237
238
239 @Override
240 public Date getDate() {
241 return new Date(startTime);
242 }
243
244
245
246
247
248
249 @XmlAttribute(name = "start")
250 public String getStartDateAsString() {
251 if (startTime == 0)
252 return null;
253 return DateTimeSupport.toUTC(startTime);
254 }
255
256
257
258
259
260
261
262 public void setStartDateAsString(String startTime) {
263 if (startTime != null && !"0".equals(startTime) && !startTime.isEmpty()) {
264 try {
265 this.startTime = DateTimeSupport.fromUTC(startTime);
266 } catch (Exception e) {
267 logger.info("Unable to parse start time {}", startTime);
268 }
269 } else {
270 this.startTime = 0;
271 }
272 }
273
274
275
276
277
278
279 @Override
280 public Iterable<MediaPackageElement> elements() {
281 return Arrays.asList(getElements());
282 }
283
284
285
286
287
288
289 @Override
290 public MediaPackageElement[] getElements() {
291 return elements.toArray(new MediaPackageElement[elements.size()]);
292 }
293
294
295
296
297
298
299 @Override
300 public MediaPackageElement getElementByReference(MediaPackageReference reference) {
301 for (MediaPackageElement e : this.elements) {
302 if (!reference.getType().equalsIgnoreCase(e.getElementType().toString()))
303 continue;
304 if (reference.getIdentifier().equals(e.getIdentifier()))
305 return e;
306 }
307 return null;
308 }
309
310
311
312
313 @Override
314 public MediaPackageElement getElementById(String id) {
315 for (MediaPackageElement element : getElements()) {
316 if (id.equals(element.getIdentifier()))
317 return element;
318 }
319 return null;
320 }
321
322
323
324
325
326
327 @Override
328 public MediaPackageElement[] getElementsByTags(Collection<String> tags) {
329 if (tags == null || tags.isEmpty())
330 return getElements();
331 Set<String> keep = new HashSet<String>();
332 Set<String> lose = new HashSet<String>();
333 for (String tag : tags) {
334 if (StringUtils.isBlank(tag))
335 continue;
336 if (tag.startsWith(NEGATE_TAG_PREFIX)) {
337 lose.add(tag.substring(NEGATE_TAG_PREFIX.length()));
338 } else {
339 keep.add(tag);
340 }
341 }
342 List<MediaPackageElement> result = new ArrayList<>();
343 for (MediaPackageElement element : getElements()) {
344 boolean add = false;
345 for (String elementTag : element.getTags()) {
346 if (lose.contains(elementTag)) {
347 add = false;
348 break;
349 } else if (keep.contains(elementTag)) {
350 add = true;
351 }
352 }
353 if (add) {
354 result.add(element);
355 }
356 }
357 return result.toArray(new MediaPackageElement[result.size()]);
358 }
359
360
361
362
363
364
365 @Override
366 public Catalog[] getCatalogsByTags(Collection<String> tags) {
367 MediaPackageElement[] matchingElements = getElementsByTags(tags);
368 List<Catalog> catalogs = new ArrayList<>();
369 for (MediaPackageElement element : matchingElements) {
370 if (Catalog.TYPE.equals(element.getElementType())) {
371 catalogs.add((Catalog) element);
372 }
373 }
374 return catalogs.toArray(new Catalog[catalogs.size()]);
375 }
376
377
378
379
380
381
382 @Override
383 public Track[] getTracksByTags(Collection<String> tags) {
384 MediaPackageElement[] matchingElements = getElementsByTags(tags);
385 List<Track> tracks = new ArrayList<>();
386 for (MediaPackageElement element : matchingElements) {
387 if (Track.TYPE.equals(element.getElementType())) {
388 tracks.add((Track) element);
389 }
390 }
391 return tracks.toArray(new Track[tracks.size()]);
392 }
393
394
395
396
397
398
399 @Override
400 public MediaPackageElement[] getElementsByFlavor(MediaPackageElementFlavor flavor) {
401 if (flavor == null)
402 throw new IllegalArgumentException("Flavor cannot be null");
403
404 List<MediaPackageElement> elements = new ArrayList<>();
405 for (MediaPackageElement element : getElements()) {
406 if (flavor.matches(element.getFlavor()))
407 elements.add(element);
408 }
409 return elements.toArray(new MediaPackageElement[elements.size()]);
410 }
411
412
413
414
415 @Override
416 public boolean contains(MediaPackageElement element) {
417 if (element == null)
418 throw new IllegalArgumentException("Media package element must not be null");
419 return (elements.contains(element));
420 }
421
422
423
424
425
426
427
428
429 boolean contains(String identifier) {
430 for (MediaPackageElement element : getElements()) {
431 if (element.getIdentifier().equals(identifier))
432 return true;
433 }
434 return false;
435 }
436
437
438
439
440 @Override
441 public void add(Catalog catalog) {
442 integrateCatalog(catalog);
443 addInternal(catalog);
444 }
445
446
447
448
449 @Override
450 public void add(Track track) {
451 integrateTrack(track);
452 addInternal(track);
453 }
454
455
456
457
458 @Override
459 public void add(Attachment attachment) {
460 integrateAttachment(attachment);
461 addInternal(attachment);
462 }
463
464
465
466
467 @Override
468 public Catalog getCatalog(String catalogId) {
469 synchronized (elements) {
470 for (MediaPackageElement e : elements) {
471 if (e.getIdentifier().equals(catalogId) && e instanceof Catalog)
472 return (Catalog) e;
473 }
474 }
475 return null;
476 }
477
478
479
480
481 @XmlElementWrapper(name = "metadata")
482 @XmlElement(name = "catalog")
483 @Override
484 public Catalog[] getCatalogs() {
485 Collection<Catalog> catalogs = loadCatalogs();
486 return catalogs.toArray(new Catalog[catalogs.size()]);
487 }
488
489 void setCatalogs(Catalog[] catalogs) {
490 List<Catalog> newCatalogs = Arrays.asList(catalogs);
491 List<Catalog> oldCatalogs = Arrays.asList(getCatalogs());
492
493 for (Catalog existing : oldCatalogs) {
494 if (!newCatalogs.contains(existing)) {
495 remove(existing);
496 }
497 }
498 for (Catalog newCatalog : newCatalogs) {
499 if (!oldCatalogs.contains(newCatalog)) {
500 add(newCatalog);
501 }
502 }
503 }
504
505
506
507
508 @Override
509 public Catalog[] getCatalogs(MediaPackageElementFlavor flavor) {
510 if (flavor == null)
511 throw new IllegalArgumentException("Unable to filter by null criterion");
512
513
514 Collection<Catalog> catalogs = loadCatalogs();
515 List<Catalog> candidates = new ArrayList<>(catalogs);
516 for (Catalog c : catalogs) {
517 if (c.getFlavor() == null || !c.getFlavor().matches(flavor)) {
518 candidates.remove(c);
519 }
520 }
521 return candidates.toArray(new Catalog[0]);
522 }
523
524
525
526
527 @Override
528 public Catalog[] getCatalogs(MediaPackageReference reference) {
529 return getCatalogs(reference, false);
530 }
531
532 private Catalog[] getCatalogs(MediaPackageReference reference, boolean includeDerived) {
533 if (reference == null)
534 throw new IllegalArgumentException("Unable to filter by null reference");
535
536
537 Collection<Catalog> catalogs = loadCatalogs();
538 List<Catalog> candidates = new ArrayList<>(catalogs);
539 for (Catalog c : catalogs) {
540 MediaPackageReference r = c.getReference();
541 if (!reference.matches(r)) {
542 boolean indirectHit = false;
543
544
545 MediaPackageReference elementRef = new MediaPackageReferenceImpl(reference.getType(), reference.getIdentifier());
546
547
548 while (includeDerived && r != null) {
549 if (r.matches(elementRef)) {
550 indirectHit = true;
551 break;
552 }
553 r = getElement(r).getReference();
554 }
555
556 if (!indirectHit)
557 candidates.remove(c);
558 }
559 }
560
561 return candidates.toArray(new Catalog[candidates.size()]);
562 }
563
564
565
566
567
568 @Override
569 public Catalog[] getCatalogs(MediaPackageElementFlavor flavor, MediaPackageReference reference) {
570 if (flavor == null)
571 throw new IllegalArgumentException("Unable to filter by null criterion");
572 if (reference == null)
573 throw new IllegalArgumentException("Unable to filter by null reference");
574
575
576 Collection<Catalog> catalogs = loadCatalogs();
577 List<Catalog> candidates = new ArrayList<>(catalogs);
578 for (Catalog c : catalogs) {
579 if (!flavor.equals(c.getFlavor()) || (c.getReference() != null && !c.getReference().matches(reference))) {
580 candidates.remove(c);
581 }
582 }
583 return candidates.toArray(new Catalog[candidates.size()]);
584 }
585
586
587
588
589
590
591 @Override
592 public Track getTrack(String trackId) {
593 synchronized (elements) {
594 for (MediaPackageElement e : elements) {
595 if (e.getIdentifier().equals(trackId) && e instanceof Track)
596 return (Track) e;
597 }
598 }
599 return null;
600 }
601
602
603
604
605
606
607 @XmlElementWrapper(name = "media")
608 @XmlElement(name = "track")
609 @Override
610 public Track[] getTracks() {
611 Collection<Track> tracks = loadTracks();
612 return tracks.toArray(new Track[tracks.size()]);
613 }
614
615 void setTracks(Track[] tracks) {
616 List<Track> newTracks = Arrays.asList(tracks);
617 List<Track> oldTracks = Arrays.asList(getTracks());
618
619 for (Track existing : oldTracks) {
620 if (!newTracks.contains(existing)) {
621 remove(existing);
622 }
623 }
624 for (Track newTrack : newTracks) {
625 if (!oldTracks.contains(newTrack)) {
626 add(newTrack);
627 }
628 }
629 }
630
631
632
633
634
635
636 @Override
637 public Track[] getTracksByTag(String tag) {
638 List<Track> result = new ArrayList<>();
639 synchronized (elements) {
640 for (MediaPackageElement e : elements) {
641 if (e instanceof Track && e.containsTag(tag))
642 result.add((Track) e);
643 }
644 }
645 return result.toArray(new Track[result.size()]);
646 }
647
648
649
650
651
652
653 @Override
654 public Track[] getTracks(MediaPackageElementFlavor flavor) {
655 if (flavor == null)
656 throw new IllegalArgumentException("Unable to filter by null criterion");
657
658
659 Collection<Track> tracks = loadTracks();
660 List<Track> candidates = new ArrayList<>(tracks);
661 for (Track a : tracks) {
662 if (a.getFlavor() == null || !a.getFlavor().matches(flavor)) {
663 candidates.remove(a);
664 }
665 }
666 return candidates.toArray(new Track[candidates.size()]);
667 }
668
669
670
671
672
673
674 @Override
675 public boolean hasTracks() {
676 synchronized (elements) {
677 for (MediaPackageElement e : elements) {
678 if (e instanceof Track)
679 return true;
680 }
681 }
682 return false;
683 }
684
685
686
687
688
689
690 @Override
691 public MediaPackageElement[] getUnclassifiedElements() {
692 return getUnclassifiedElements(null);
693 }
694
695 private MediaPackageElement[] getUnclassifiedElements(MediaPackageElementFlavor flavor) {
696 List<MediaPackageElement> unclassifieds = new ArrayList<>();
697 synchronized (elements) {
698 for (MediaPackageElement e : elements) {
699 if (!(e instanceof Attachment) && !(e instanceof Catalog) && !(e instanceof Track)) {
700 if (flavor == null || flavor.equals(e.getFlavor())) {
701 unclassifieds.add(e);
702 }
703 }
704 }
705 }
706 return unclassifieds.toArray(new MediaPackageElement[unclassifieds.size()]);
707 }
708
709
710
711
712
713
714 @Override
715 public Attachment getAttachment(String attachmentId) {
716 synchronized (elements) {
717 for (MediaPackageElement e : elements) {
718 if (e.getIdentifier().equals(attachmentId) && e instanceof Attachment)
719 return (Attachment) e;
720 }
721 }
722 return null;
723 }
724
725
726
727
728
729
730 @XmlElementWrapper(name = "attachments")
731 @XmlElement(name = "attachment")
732 @Override
733 public Attachment[] getAttachments() {
734 Collection<Attachment> attachments = loadAttachments();
735 return attachments.toArray(new Attachment[attachments.size()]);
736 }
737
738 void setAttachments(Attachment[] catalogs) {
739 List<Attachment> newAttachments = Arrays.asList(catalogs);
740 List<Attachment> oldAttachments = Arrays.asList(getAttachments());
741
742 for (Attachment existing : oldAttachments) {
743 if (!newAttachments.contains(existing)) {
744 remove(existing);
745 }
746 }
747 for (Attachment newAttachment : newAttachments) {
748 if (!oldAttachments.contains(newAttachment)) {
749 add(newAttachment);
750 }
751 }
752 }
753
754
755
756
757
758
759 @Override
760 public Attachment[] getAttachments(MediaPackageElementFlavor flavor) {
761 if (flavor == null)
762 throw new IllegalArgumentException("Unable to filter by null criterion");
763
764
765 Collection<Attachment> attachments = loadAttachments();
766 List<Attachment> candidates = new ArrayList<>(attachments);
767 for (Attachment a : attachments) {
768 if (a.getFlavor() == null || !a.getFlavor().matches(flavor)) {
769 candidates.remove(a);
770 }
771 }
772 return candidates.toArray(new Attachment[candidates.size()]);
773 }
774
775
776
777
778
779
780 @XmlElementWrapper(name = "publications")
781 @XmlElement(name = "publication")
782 @Override
783 public Publication[] getPublications() {
784
785
786
787
788 return elements.stream()
789 .filter(Publication.class::isInstance)
790 .map(Publication.class::cast)
791 .toArray(Publication[]::new);
792 }
793
794 void setPublications(Publication[] publications) {
795 List<Publication> newPublications = Arrays.asList(publications);
796 List<Publication> oldPublications = Arrays.asList(getPublications());
797 for (Publication oldp : oldPublications) {
798 if (!newPublications.contains(oldp)) {
799 remove(oldp);
800 }
801 }
802 for (Publication newp : newPublications) {
803 if (!oldPublications.contains(newp)) {
804 add(newp);
805 }
806 }
807 }
808
809
810
811
812
813
814 @Override
815 public MediaPackageElement removeElementById(String id) {
816 MediaPackageElement element = getElementById(id);
817 if (element == null)
818 return null;
819 remove(element);
820 return element;
821 }
822
823
824
825
826
827
828 @Override
829 public void remove(MediaPackageElement element) {
830 removeElement(element);
831 }
832
833
834
835
836
837
838 @Override
839 public void remove(Attachment attachment) {
840 removeElement(attachment);
841 }
842
843
844
845
846
847
848 @Override
849 public void remove(Catalog catalog) {
850 removeElement(catalog);
851 }
852
853
854
855
856
857
858 @Override
859 public void remove(Track track) {
860 removeElement(track);
861 recalculateDuration();
862 }
863
864
865
866
867
868
869
870 void removeElement(MediaPackageElement element) {
871 removeInternal(element);
872 if (element instanceof AbstractMediaPackageElement) {
873 ((AbstractMediaPackageElement) element).setMediaPackage(null);
874 }
875 }
876
877
878
879
880
881
882 @Override
883 public MediaPackageElement add(URI url) {
884 if (url == null)
885 throw new IllegalArgumentException("Argument 'url' may not be null");
886
887 if (mediaPackageElementBuilder == null) {
888 mediaPackageElementBuilder = MediaPackageElementBuilderFactory.newInstance().newElementBuilder();
889 }
890 MediaPackageElement element = mediaPackageElementBuilder.elementFromURI(url);
891 integrate(element);
892 addInternal(element);
893 return element;
894 }
895
896
897
898
899
900
901 @Override
902 public MediaPackageElement add(URI uri, Type type, MediaPackageElementFlavor flavor) {
903 if (uri == null)
904 throw new IllegalArgumentException("Argument 'url' may not be null");
905 if (type == null)
906 throw new IllegalArgumentException("Argument 'type' may not be null");
907
908 if (mediaPackageElementBuilder == null) {
909 mediaPackageElementBuilder = MediaPackageElementBuilderFactory.newInstance().newElementBuilder();
910 }
911 MediaPackageElement element = mediaPackageElementBuilder.elementFromURI(uri, type, flavor);
912 integrate(element);
913 addInternal(element);
914 return element;
915 }
916
917
918
919
920
921
922 @Override
923 public void add(MediaPackageElement element) {
924 if (element.getElementType().equals(MediaPackageElement.Type.Track) && element instanceof Track) {
925 integrateTrack((Track) element);
926 } else if (element.getElementType().equals(MediaPackageElement.Type.Catalog) && element instanceof Catalog) {
927 integrateCatalog((Catalog) element);
928 } else if (element.getElementType().equals(MediaPackageElement.Type.Attachment) && element instanceof Attachment) {
929 integrateAttachment((Attachment) element);
930 } else {
931 integrate(element);
932 }
933 addInternal(element);
934 }
935
936
937
938
939
940
941
942 @Override
943 public void addDerived(MediaPackageElement derivedElement, MediaPackageElement sourceElement) {
944 addDerived(derivedElement, sourceElement, null);
945 }
946
947 private void addDerived(
948 MediaPackageElement derivedElement, MediaPackageElement sourceElement, Map<String, String> properties) {
949 if (derivedElement == null)
950 throw new IllegalArgumentException("The derived element is null");
951 if (sourceElement == null)
952 throw new IllegalArgumentException("The source element is null");
953 if (!contains(sourceElement))
954 throw new IllegalStateException("The sourceElement needs to be part of the media package");
955
956 derivedElement.referTo(sourceElement);
957 addInternal(derivedElement);
958
959 if (properties != null) {
960 MediaPackageReference ref = derivedElement.getReference();
961 for (Map.Entry<String, String> entry : properties.entrySet()) {
962 ref.setProperty(entry.getKey(), entry.getValue());
963 }
964 }
965 }
966
967
968
969
970
971
972
973 @Override
974 public MediaPackageElement[] getDerived(MediaPackageElement sourceElement, MediaPackageElementFlavor derivateFlavor) {
975 if (sourceElement == null)
976 throw new IllegalArgumentException("Source element cannot be null");
977 if (derivateFlavor == null)
978 throw new IllegalArgumentException("Derivate flavor cannot be null");
979
980 MediaPackageReference reference = new MediaPackageReferenceImpl(sourceElement);
981 List<MediaPackageElement> elements = new ArrayList<>();
982 for (MediaPackageElement element : getElements()) {
983 if (derivateFlavor.equals(element.getFlavor()) && reference.equals(element.getReference()))
984 elements.add(element);
985 }
986 return elements.toArray(new MediaPackageElement[elements.size()]);
987 }
988
989
990
991
992
993
994
995
996 private void integrate(MediaPackageElement element) {
997 if (element instanceof AbstractMediaPackageElement)
998 ((AbstractMediaPackageElement) element).setMediaPackage(this);
999 }
1000
1001
1002
1003
1004
1005
1006
1007
1008 private void integrateCatalog(Catalog catalog) {
1009
1010 String id = catalog.getIdentifier();
1011 if (id == null || contains(id)) {
1012 catalog.generateIdentifier();
1013 }
1014 integrate(catalog);
1015 }
1016
1017
1018
1019
1020
1021
1022
1023
1024 private void integrateTrack(Track track) {
1025
1026 String id = track.getIdentifier();
1027 if (id == null || contains(id)) {
1028 track.generateIdentifier();
1029 }
1030 integrate(track);
1031 }
1032
1033
1034
1035
1036
1037
1038
1039
1040 private void integrateAttachment(Attachment attachment) {
1041
1042 String id = attachment.getIdentifier();
1043 if (id == null || contains(id)) {
1044 attachment.generateIdentifier();
1045 }
1046 integrate(attachment);
1047 }
1048
1049
1050
1051
1052 @Override
1053 public void verify() throws MediaPackageException {
1054 for (MediaPackageElement e : getElements()) {
1055 e.verify();
1056 }
1057 }
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070 public static MediaPackageImpl valueOf(String xml) throws MediaPackageException {
1071 return MediaPackageImpl.valueOf(IOUtils.toInputStream(xml, "UTF-8"));
1072 }
1073
1074
1075
1076
1077
1078 @Override
1079 public int hashCode() {
1080 return getIdentifier().hashCode();
1081 }
1082
1083
1084
1085
1086 @Override
1087 public boolean equals(Object obj) {
1088 if (obj instanceof MediaPackage) {
1089 return getIdentifier().equals(((MediaPackage) obj).getIdentifier());
1090 }
1091 return false;
1092 }
1093
1094
1095
1096
1097
1098
1099 @Override
1100 public Object clone() {
1101 try {
1102 String xml = MediaPackageParser.getAsXml(this);
1103 return MediaPackageBuilderFactory.newInstance().newMediaPackageBuilder().loadFromXml(xml);
1104 } catch (Exception e) {
1105 throw new RuntimeException(e);
1106 }
1107 }
1108
1109
1110
1111
1112 @Override
1113 public String toString() {
1114 if (identifier != null)
1115 return identifier.toString();
1116 else
1117 return "Unknown media package";
1118 }
1119
1120
1121
1122
1123 public static class Adapter extends XmlAdapter<MediaPackageImpl, MediaPackage> {
1124 @Override
1125 public MediaPackageImpl marshal(MediaPackage mp) throws Exception {
1126 return (MediaPackageImpl) mp;
1127 }
1128
1129 @Override
1130 public MediaPackage unmarshal(MediaPackageImpl mp) throws Exception {
1131 return mp;
1132 }
1133 }
1134
1135
1136
1137
1138
1139
1140
1141
1142 public static MediaPackageImpl valueOf(InputStream xml) throws MediaPackageException {
1143 try {
1144 Unmarshaller unmarshaller = context.createUnmarshaller();
1145 return unmarshaller.unmarshal(XmlSafeParser.parse(xml), MediaPackageImpl.class).getValue();
1146 } catch (JAXBException e) {
1147 throw new MediaPackageException(e.getLinkedException() != null ? e.getLinkedException() : e);
1148 } catch (IOException | SAXException e) {
1149 throw new MediaPackageException(e);
1150 } finally {
1151 IoSupport.closeQuietly(xml);
1152 }
1153 }
1154
1155
1156
1157
1158
1159
1160
1161
1162 public static MediaPackageImpl valueOf(Node xml) throws MediaPackageException {
1163 try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
1164 Unmarshaller unmarshaller = context.createUnmarshaller();
1165
1166 DOMImplementationRegistry reg = DOMImplementationRegistry.newInstance();
1167 DOMImplementationLS impl = (DOMImplementationLS) reg.getDOMImplementation("LS");
1168 LSSerializer serializer = impl.createLSSerializer();
1169 serializer.getDomConfig().setParameter("comments", false);
1170 serializer.getDomConfig().setParameter("format-pretty-print", false);
1171 LSOutput output = impl.createLSOutput();
1172 output.setEncoding("UTF-8");
1173 output.setByteStream(out);
1174
1175 serializer.write(xml, output);
1176
1177 try (InputStream in = new ByteArrayInputStream(out.toByteArray())) {
1178
1179
1180 return unmarshaller.unmarshal(new StreamSource(in), MediaPackageImpl.class).getValue();
1181
1182 }
1183 } catch (Exception e) {
1184 throw new MediaPackageException("Error deserializing media package node", e);
1185 }
1186 }
1187
1188
1189
1190
1191
1192
1193 @Override
1194 public String[] getContributors() {
1195 if (contributors == null)
1196 return new String[] {};
1197 return contributors.toArray(new String[contributors.size()]);
1198 }
1199
1200
1201
1202
1203
1204
1205 @Override
1206 public String[] getCreators() {
1207 if (creators == null)
1208 return new String[] {};
1209 return creators.toArray(new String[creators.size()]);
1210 }
1211
1212
1213
1214
1215
1216
1217 @Override
1218 public String getLanguage() {
1219 return language;
1220 }
1221
1222
1223
1224
1225
1226
1227 @Override
1228 public String getLicense() {
1229 return license;
1230 }
1231
1232
1233
1234
1235
1236
1237 @Override
1238 public String getSeries() {
1239 return series;
1240 }
1241
1242
1243
1244
1245
1246
1247 @Override
1248 public String[] getSubjects() {
1249 if (subjects == null)
1250 return new String[] {};
1251 return subjects.toArray(new String[subjects.size()]);
1252 }
1253
1254
1255
1256
1257
1258
1259 @Override
1260 public String getTitle() {
1261 return title;
1262 }
1263
1264
1265
1266
1267
1268
1269 @Override
1270 public String getSeriesTitle() {
1271 return seriesTitle;
1272 }
1273
1274
1275
1276
1277
1278
1279 @Override
1280 public void setSeriesTitle(String seriesTitle) {
1281 this.seriesTitle = seriesTitle;
1282 }
1283
1284
1285
1286
1287
1288
1289 @Override
1290 public void addContributor(String contributor) {
1291 if (contributors == null)
1292 contributors = new TreeSet<String>();
1293 contributors.add(contributor);
1294 }
1295
1296
1297
1298
1299
1300
1301 @Override
1302 public void addCreator(String creator) {
1303 if (creators == null)
1304 creators = new TreeSet<>();
1305 creators.add(creator);
1306 }
1307
1308
1309
1310
1311
1312
1313 @Override
1314 public void addSubject(String subject) {
1315 if (subjects == null)
1316 subjects = new TreeSet<>();
1317 subjects.add(subject);
1318 }
1319
1320
1321
1322
1323
1324
1325 @Override
1326 public void removeContributor(String contributor) {
1327 if (contributors != null)
1328 contributors.remove(contributor);
1329 }
1330
1331
1332
1333
1334
1335
1336 @Override
1337 public void removeCreator(String creator) {
1338 if (creators != null)
1339 creators.remove(creator);
1340 }
1341
1342
1343
1344
1345
1346
1347 @Override
1348 public void removeSubject(String subject) {
1349 if (subjects != null)
1350 subjects.remove(subject);
1351 }
1352
1353
1354
1355
1356
1357
1358 @Override
1359 public void setDate(Date date) {
1360 if (date != null)
1361 this.startTime = date.getTime();
1362 else
1363 this.startTime = 0;
1364 }
1365
1366
1367
1368
1369
1370
1371 @Override
1372 public void setLanguage(String language) {
1373 this.language = language;
1374 }
1375
1376
1377
1378
1379
1380
1381 @Override
1382 public void setLicense(String license) {
1383 this.license = license;
1384 }
1385
1386
1387
1388
1389
1390
1391 @Override
1392 public void setSeries(String identifier) {
1393 this.series = identifier;
1394 }
1395
1396
1397
1398
1399
1400
1401 @Override
1402 public void setTitle(String title) {
1403 this.title = title;
1404 }
1405
1406 @Override
1407 public boolean isLive() {
1408 return Arrays.stream(getTracks()).anyMatch(Track::isLive);
1409 }
1410
1411
1412
1413
1414
1415
1416
1417
1418 MediaPackageElement getElement(MediaPackageReference reference) {
1419 if (reference == null)
1420 return null;
1421 for (MediaPackageElement e : elements) {
1422 if (e.getIdentifier().equals(reference.getIdentifier()))
1423 return e;
1424 }
1425 return null;
1426 }
1427
1428
1429
1430
1431
1432
1433
1434 private void addInternal(MediaPackageElement element) {
1435 if (element == null)
1436 throw new IllegalArgumentException("Media package element must not be null");
1437
1438 elements.add(element);
1439 if (element instanceof Track) {
1440 recalculateDuration();
1441 }
1442
1443
1444 if (element.getIdentifier() == null) {
1445 element.generateIdentifier();
1446 }
1447 }
1448
1449
1450
1451
1452
1453
1454
1455 void removeInternal(MediaPackageElement element) {
1456 if (element == null)
1457 throw new IllegalArgumentException("Media package element must not be null");
1458
1459 if (elements.remove(element) && element instanceof Track) {
1460 recalculateDuration();
1461 }
1462 }
1463
1464
1465
1466
1467
1468
1469 private Collection<Track> loadTracks() {
1470 List<Track> tracks = new ArrayList<>();
1471 synchronized (elements) {
1472 for (MediaPackageElement e : elements) {
1473 if (e instanceof Track) {
1474 tracks.add((Track) e);
1475 }
1476 }
1477 }
1478 return tracks;
1479 }
1480
1481
1482
1483
1484
1485
1486 private Collection<Catalog> loadCatalogs() {
1487 List<Catalog> catalogs = new ArrayList<>();
1488 synchronized (elements) {
1489 for (MediaPackageElement e : elements) {
1490 if (e instanceof Catalog) {
1491 catalogs.add((Catalog) e);
1492 }
1493 }
1494 }
1495 return catalogs;
1496 }
1497
1498
1499
1500
1501
1502
1503 private Collection<Attachment> loadAttachments() {
1504 List<Attachment> attachments = new ArrayList<>();
1505 synchronized (elements) {
1506 for (MediaPackageElement e : elements) {
1507 if (e instanceof Attachment) {
1508 attachments.add((Attachment) e);
1509 }
1510 }
1511 }
1512 return attachments;
1513 }
1514 }