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

nl.vpro.domain.media.MediaTable 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.*;
import lombok.extern.slf4j.Slf4j;

import java.io.Serial;
import java.io.Serializable;
import java.time.Instant;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import jakarta.validation.Valid;
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.bind.annotation.*;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.meeuw.math.abstractalgebra.Streamable;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;

import nl.vpro.jackson2.StringInstantToJsonTimestamp;
import nl.vpro.xml.bind.InstantXmlAdapter;

import static nl.vpro.domain.media.MediaObjects.deepCopy;


@XmlRootElement(name = "mediaInformation")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "mediaTableType",
         propOrder = {
             "programTable",
             "groupTable",
             "locationTable",
             "schedule"}
)
@lombok.Builder
@AllArgsConstructor
@Slf4j
public class MediaTable implements Iterable, Serializable, Streamable {

    @Serial
    private static final long serialVersionUID = 4054512453318247403L;

    public MediaTable() {
    }

    @XmlElementWrapper(name = "programTable")
    @XmlElement(name = "program")
    protected List<@Valid Program> programTable;

    @XmlElementWrapper(name = "groupTable")
    @XmlElement(name = "group")
    protected List<@Valid Group> groupTable;

    @XmlElement
    @Getter
    @Setter
    @Valid
    protected LocationTable locationTable;

    @XmlElement
    @Setter
    @Valid
    protected Schedule schedule;

    @XmlAttribute
    @Getter
    @Setter
    @XmlJavaTypeAdapter(InstantXmlAdapter.class)
    @XmlSchemaType(name = "dateTime")
    @JsonDeserialize(using = StringInstantToJsonTimestamp.Deserializer.class)
    @JsonSerialize(using = StringInstantToJsonTimestamp.Serializer.class)
    protected Instant publicationTime;

    @XmlAttribute
    @Getter
    @Setter
    protected String publisher;

    @XmlAttribute
    @Getter
    @Setter
    protected Short version;

    @XmlAttribute
    @Getter
    @Setter
    protected String source;

    /**
     * @since 5.9
     */
    public MediaTable add(MediaObject mo) {
        if (mo instanceof Program program) {
            return addProgram(program);
        } else if (mo instanceof Group group) {
            return addGroup(group);
        } else {
            log.warn("Could not add {}", mo);
            return this;
        }
    }

    public MediaTable addProgram(Program program) {
        if(programTable == null) {
            programTable = new ArrayList<>();
        }

        programTable.add(program);
        return this;
    }

    public MediaTable add(MediaTable mo) {
        for (Program p : mo.getProgramTable()) {
            addProgram(p);
        }
        for (Group g : mo.getGroupTable()) {
            addGroup(g);
        }
        return this;
    }

    /**
     * Searches the mediaobject with given mid in the table. This may return a {@link Program}, {@link Group}, or {@link Segment}
     */
    @SuppressWarnings("unchecked")
    public  Optional find(String mid) {
        for (MediaObject p : Iterables.concat(getProgramTable(), getGroupTable())) {
            if (Objects.equals(p.getMid(), mid)) {
                return Optional.of((T) p);
            }
            if (p instanceof Program) {
                for (Segment s : ((Program) p).getSegments()) {
                    if (Objects.equals(s.getMid(), mid)) {
                        return Optional.of((T) s);
                    }
                }
            }
        }
        return Optional.empty();
    }

    /**
     * @since 5.11
     */
    @SuppressWarnings("unchecked")
    public  Optional findByCrid(String crid) {
        for (MediaObject p : Iterables.concat(getProgramTable(), getGroupTable())) {
            if (p.getCrids().contains(crid)) {
                return Optional.of((T) p);
            }
        }
        return Optional.empty();
    }

    /**
     * @since 5.34
     */
    public Optional getGroup(String mid) {
        return getGroupTable().stream()
            .filter(g -> mid.equals(g.getMid()))
            .findFirst();
    }

    /**
     * @since 5.34
     */
    public Optional getProgram(String mid) {
        return getProgramTable().stream()
            .filter(p -> mid.equals(p.getMid()))
            .findFirst();
    }

    /**
     * Returns the schedule associated with this table. If there is none, then it will be a schedule based on all {@link ScheduleEvent}s of all {@link #getProgramTable()}.
     */

    public Schedule getSchedule() {
        if (schedule == null) {
            Schedule s = new Schedule();
            for (Program program : getProgramTable()) {
                for (ScheduleEvent event : program.getScheduleEvents()) {
                    s.addScheduleEvent(event);
                }
            }
            return s;

        }
        return schedule;
    }

    /**
     * @since 5.9
     */
    public boolean contains(String mid) {
        return find(mid).isPresent();
    }

    public List getProgramTable() {
        if(programTable == null) {
            programTable = new ArrayList<>();
        }
        return programTable;
    }

    public void setProgramTable(List programTable) {
        this.programTable = programTable;
    }

    public MediaTable addGroup(Group group) {
        if(groupTable == null) {
            groupTable = new ArrayList<>();
        }

        groupTable.add(group);
        return this;
    }

    public List getGroupTable() {
        if(groupTable == null) {
            groupTable = new ArrayList<>();
        }
        return this.groupTable;
    }

    public void setGroupTable(List groupTable) {
        this.groupTable = groupTable;
    }

    @Override
    public String toString() {
        return "MediaTable " + getGroupTable().size() + " groups " + getProgramTable().size() + " program " + getSchedule();
    }

    @NonNull
    @Override
    public Iterator iterator() {
        return Iterators.concat(
            getProgramTable().listIterator(),
            getGroupTable().listIterator()
        );
    }

    void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        mergePrograms();
        linkSchedule();
    }

    private void mergePrograms() {
        // if you provide a media table xml with duplicate programs, (with same mid), then we simply take only the
        // merge duplicate programs
        Map> map = new HashMap<>();
        for (Program program : programTable) {
            if (program.getMid() != null) {
                List programs = map.computeIfAbsent(program.getMid(), k -> new ArrayList<>());
                programs.add(program);
            }
        }
        for (Map.Entry>  e : map.entrySet()) {
            if (e.getValue().size() > 1) {
                Program first = e.getValue().get(0);
                log.debug("Found duplicate program {}", first);
                for (int i = 1; i < e.getValue().size(); i++) {
                    Program another = e.getValue().get(i);
                    for (String c : another.getCrids()) {
                        if (! first.getCrids().contains(c)) {
                            first.getCrids().add(c);
                        }
                    }
                    another.setMid(null); // to break equalsOnMid
                    programTable.remove(another);
                    log.debug("Removing {}", another);
                }
            }
        }
    }

    private void linkSchedule() {
        List programs = programTable;
        if (schedule.scheduleEvents != null) {
            for (ScheduleEvent scheduleEvent : schedule.scheduleEvents) {
                Program clone = null;

                for (Program program : programs) {
                    if (!program.getCrids().isEmpty()
                        && StringUtils.isNotEmpty(scheduleEvent.getUrnRef())
                        && program.getCrids().contains(scheduleEvent.getUrnRef())) {

                        // MIS TVAnytime stores poProgId's under events. Therefore MIS deliveries may contain two or more
                        // ScheduleEvents referencing the same program on crid with different poProgId's.
                        // See test case.

                        String scheduleEventPoProgID = scheduleEvent.getPoProgID();
                        log.debug("No poprogid for {}", scheduleEvent);
                        if (scheduleEventPoProgID != null) {
                            if (program.getMid() == null || scheduleEventPoProgID.equals(program.getMid())) {
                                program.setMid(scheduleEventPoProgID);
                                scheduleEvent.setParent(program);
                            } else {
                                log.debug("Cloning a MIS duplicate");
                                // Create a clone for the second poProgId and its event
                                clone = cloneMisDuplicate(program);
                                /* Reset MID to null first, then set it to the poProgID from the Schedule event; otherwise an
                                     IllegalArgumentException will be thrown setting the MID to another value.
                                    */
                                clone.setMid(null);
                                clone.setMid(scheduleEventPoProgID);
                                scheduleEvent.setParent(clone);
                            }
                        } else {
                            scheduleEvent.setParent(program);
                        }
                        break;
                    }
                }

                if (clone != null) {
                    programs.add(clone);
                }
            }
        }
    }


    private Program cloneMisDuplicate(Program program) {
        Program clone = deepCopy(program);

        // Prevent constraint violation on duplicate crids
        for (String crid : clone.getCrids()) {
            clone.removeCrid(crid);
        }

        // Do not copy events
        List events = new ArrayList<>(clone.getScheduleEvents());
        for (ScheduleEvent event : events) {
            event.clearMediaObject();
        }

        return clone;
    }

    public int size() {
        return getProgramTable().size() + getGroupTable().size();
    }

    @Override
    public Stream stream() {
        return StreamSupport.stream(Spliterators.spliterator(iterator(), size(), Spliterator.ORDERED), false);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy