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