All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.vpro.domain.media.Segment Maven / Gradle / Ivy

Go to download

The basic domain classes for 'media', the core of POMS. Also, the 'update' XML bindings for it. It also contains some closely related domain classes like the enum to contain NICAM kijkwijzer settings.

There is a newer version: 8.3.1
Show newest version
package nl.vpro.domain.media;

import lombok.Getter;
import lombok.Setter;

import java.io.Serial;
import java.time.Instant;
import java.util.*;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.bind.annotation.*;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import org.checkerframework.checker.nullness.qual.NonNull;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import nl.vpro.domain.Child;
import nl.vpro.domain.media.support.*;
import nl.vpro.jackson2.Views;
import nl.vpro.jackson2.XMLDurationToJsonTimestamp;
import nl.vpro.validation.SegmentValidation;
import nl.vpro.xml.bind.DurationXmlAdapter;

/**
 * A segment is a view on a program, representing a part of if starting at a given {@link #getStart()} after the beginning of the program (and with a shorter {@link #getDuration()}. It cannot exist alone, and always has a {@link #getParent()}, which always is a {@link Program}.
 */

@Entity
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "segment")
@XmlType(name = "segmentType", propOrder = {
    "segmentOf",
    "start"
})
@JsonTypeName("segment")
@SegmentValidation
public final class Segment extends MediaObject implements Comparable, Child, MutableOwnable {

    @Serial
    private static final long serialVersionUID = -868293795041160925L;

    static boolean defaultCorrelation = false;

    public static MediaBuilder.SegmentBuilder builder() {
        return MediaBuilder.segment();
    }
    /**
     * Unset some default values, to ensure that roundtripping will result same object
     * @since 5.11
     */
    @JsonCreator
    static Segment jsonCreator() {
        return builder().workflow(null).creationDate((Instant) null).build();
    }

    @ManyToOne(targetEntity = Program.class, optional = false, fetch = FetchType.LAZY)
    Program parent;

    @Setter
    @Column(nullable = false)
    @NotNull(message = "start property is required")
    private java.time.Duration start;


    @Transient
    private String urnRef;

    @Transient
    private String midRef;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    @NotNull(message = "no segment type given")
    private SegmentType type = SegmentType.SEGMENT;

    @Getter
    @Setter
    @Enumerated(EnumType.STRING)
    private OwnerType owner;

    @Setter
    @Transient
    private RecursiveMemberRef segmentOf;

    public Segment() {
    }

    public Segment(@NonNull Program program, String midRef, java.time.Duration start, AuthorizedDuration duration) {
        this.start = start;
        this.duration = duration;
        avType = program.getAVType();
        program.addSegment(this);
        this.midRef = midRef == null ? program.getMid() : midRef;
    }

    public Segment(Program program) {
        this(program, program.getMid(), java.time.Duration.ZERO, program.getDuration());
    }

    public Segment(Program program, java.time.Duration start, java.time.Duration duration) {
        this(program, program.getMid(), start, new AuthorizedDuration(duration));
    }

    public Segment(Program program, java.time.Duration start, AuthorizedDuration duration) {
        this(program, program.getMid(), start, duration);
    }

    public Segment(String mid, Program program, java.time.Duration start, AuthorizedDuration duration) {
        this(program, program.getMid(), start, duration);
        this.mid = mid;
    }

    public Segment(AVType avType) {
        this.avType = avType;
    }

    public Segment(AVType avType, java.time.Duration start) {
        this.avType = avType;
        this.start = start;
    }

    public Segment(AVType avType, java.time.Duration start, java.time.Duration duration) {
        this(avType, start, new AuthorizedDuration(duration));
    }

    public Segment(AVType avType, java.time.Duration start, AuthorizedDuration duration) {
        this.avType = avType;
        this.start = start;
        this.duration = duration;
    }

    public Segment(Segment source) {
        super(source);
        this.start = source.start;
        this.midRef = source.midRef;
        this.urnRef = source.urnRef;
    }

    public static Segment copy(Segment source) {
        if (source == null) {
            return null;
        }
        return new Segment(source);
    }


    @Override
    public boolean isActivation(Instant now) {
        return getParent().isActivation(now) || super.isActivation(now);
    }

    @Override
    public boolean isDeactivation(Instant now) {
        return getParent().isDeactivation(now) || super.isDeactivation(now);
    }

    @Override
    public boolean isRevocable(Instant now) {
        if(super.isRevocable(now)) {
            return true;
        }

        return getParent().isRevocable(now);
    }

    /**
     * A segment is considered merged if its parent is.
     * 

* Note that generally it won't have {@link MediaObject#getMergedTo()} * @see MediaObject#isMerged() */ @Override public boolean isMerged() { return (parent != null && parent.isMerged()) || super.isMerged(); } /** * If a segment is member of a merged {@link Program} then its workflow will become {@link Workflow#MERGED}, to indicate that. But this will in all cases be considered {@link MediaObject#isDeleted()} *

* E.g. if such on object is requested from the API it should result 404. */ @Override public boolean isDeleted() { return super.isDeleted() || isMerged(); } /** * Returns the parent {@link Program} of this segment. Not that this does not work directly after a simple unmarshall of * an individual segment because the full program object simply is not available then. *

* Use {@link #getMidRef()} for the mid, and obtain it separately. */ @Override public Program getParent() { return parent; } @Override public void setParent(Program parent) { if(parent == null && this.parent != null) { throw new IllegalArgumentException("Cannot set parent to null"); } this.parent = parent; invalidateSortDate(); this.midRef = null; } @XmlAttribute(required = true) public String getUrnRef() { if(parent != null) { return parent.getUrn(); } return urnRef; } public void setUrnRef(String urnRef) { if(parent != null) { throw new IllegalStateException("This segments program holds the urnRef for this segment"); } this.urnRef = urnRef; } /** * @since 1.9 */ @XmlAttribute(required = true) public String getMidRef() { if(parent != null) { return parent.getMid(); } return midRef; } /** * @since 1.9 */ public void setMidRef(String midRef) { if(parent != null) { if (midRef != null) { if (Objects.equals(parent.getMid(), midRef)) { return; } else { throw new IllegalStateException("This segments program holds the midRef for this segment"); } } if (segmentOf != null) { segmentOf.setMidRef(midRef); } } this.midRef = midRef; } @Override protected String getUrnPrefix() { return SegmentType.URN_PREFIX; } @XmlElement(required = true) @XmlJavaTypeAdapter(DurationXmlAdapter.class) @JsonSerialize(using = XMLDurationToJsonTimestamp.Serializer.class) @JsonInclude(JsonInclude.Include.NON_NULL) @JsonDeserialize(using = XMLDurationToJsonTimestamp.DeserializerJavaDuration.class) public java.time.@NonNull Duration getStart() { return start; } @Override public SortedSet getAncestors() { final SortedSet ancestors = super.getAncestors(); if(parent != null) { ancestors.add(parent); ancestors.addAll(parent.getAncestors()); } return ancestors; } @Override public boolean hasAncestor(MediaObject ancestor) { return (super.hasAncestor(ancestor) || parent != null) && parent.hasAncestor(ancestor); } @Override protected void findAncestry(MediaObject ancestor, List ancestors) { super.findAncestry(ancestor, ancestors); if(ancestors.isEmpty() && parent != null) { parent.findAncestry(ancestor, ancestors); ancestors.add(parent); } } @Override public int compareTo(@NonNull Segment o) { if(super.equals(o)) { return 0; } if(this.start != null && o.start != null) { int compare = this.start.compareTo(o.getStart()); if (compare != 0) { return compare; } } if(this.type != null && o.type != null) { int compare = this.type.compareTo(o.getType()); if (compare != 0) { return compare; } } { int compare = Objects.compare(this.getMainTitle(), o.getMainTitle(), Comparator.nullsLast(Comparator.naturalOrder())); if (compare != 0) { return compare; } } if (this.getMid() != null && o.getMid() != null) { int compare = this.getMid().compareTo(o.getMid()); if (compare != 0) { return compare; } } if (this.getId() != null && o.getId() != null) { int compare = this.getId().compareTo(o.getId()); if (compare != 0) { return compare; } } return o.hashCode() - hashCode(); } @XmlAttribute(required = true) @NotNull @Override public SegmentType getType() { return type; } @Override public void setMediaType(MediaType type) { setType(type == null ? null : (SegmentType) type.getSubType()); } public void setType(SegmentType segmentType) { if(segmentType == null) { segmentType = SegmentType.SEGMENT; } this.type = segmentType; } @Override void afterUnmarshal(Unmarshaller unmarshaller, Object parent) { super.afterUnmarshal(unmarshaller, parent); if(parent instanceof Program program) { this.parent = program; this.urnRef = null; this.midRef = null; } } /** * The correlation id of a segment currently is the correlation id of its _parent_. *

* I forgot why this is important, but I think it may give problems when a segment is converted to a clip and/or vice versa. *

* For 7.1 release we'll leave it */ @Override public Correlation calcCorrelation() { if (defaultCorrelation) { return super.calcCorrelation(); } else { if (parent != null) { return parent.getCorrelation(); } String midRef = getMidRef(); if (midRef != null) { return Correlation.mid(midRef); } else { return super.calcCorrelation(); } } } @XmlElement(name = "segmentOf") @JsonView(Views.Forward.class) public RecursiveMemberRef getSegmentOf() { if (segmentOf == null) { if (parent != null) { segmentOf = RecursiveMemberRef.ofSegment(this); } else if (getMidRef() != null) { MediaType type = getDescendantOf().stream().filter(m -> m.midRef.equals(getMidRef())).map(DescendantRef::getType).findFirst().orElse(MediaType.PROGRAM); segmentOf = RecursiveMemberRef.builder() .childMid(getMid()) .parentMid(getMidRef()) .parentType(type) .build(); } } else { if (segmentOf.getChildMid() == null) { // this is not in the serialization segmentOf.setChildMid(getMid()); } } return segmentOf; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy