• Concert.java

  • §
    package MusicLandscape.entities;
    
    import java.util.Arrays;
  • §

    A Concert is an Event with a set list of tracks.

    The set list is an array, and the class can dynamically extend this array if more tracks are added.

    Interesting things about this class:

    • the impact and toString methods from Event are overridden
    • getSetList and setSetList clone the Track objects!
    /**
     * Represents a concert of a certain artist with a certain set list as a specific event.
     * <p>
     * As an extension of a generic event a concert provides the possibility to store the setlist.
     * The setlist is the sequence of non-null tracks played at a concert.
     * Class concert provides methods to add tracks to the (end of the) tracklist and to reset the tracklist
     * all together (empty it).
     *
     * @author Jonas Altrock (ew20b126@technikum-wien.at)
     * @version 1
     * @since ExerciseSheet03
     */
    public class Concert extends Event {
        /**
         * next free index
         */
        private int nextIdx = 0;
    
        /**
         * array holding the tracks of the setlist
         */
        private Track[] setList;
    
        public Concert() {
            setList = new Track[1];
        }
    
        /**
         * adds a track to the set list
         * <p>
         * Tracks are added to the end of the list with the first track played at the concert being stored at the
         * beginning of the list.
         * This method returns whether the non-null track was successfully added to the setlist or not.
         * This method does not accept/ignores null tracks.
         *
         * @param t the track to add
         * @return true if the track was added, false otherwise
         */
        public boolean addTrack(Track t) {
            if (t == null) {
                return false;
            }
    
            ensureCapacity(nextIdx + 1);
            setList[nextIdx++] = t;
    
            return true;
        }
    
        /**
         * ensures sufficient storage for a specific number of tracks in the setlist
         * <p>
         * If the requested capacity can not be ensured before the call, this method increases storage thereby
         * keeping all existing entries.
         *
         * @param length the maximum number of tracks this concert must be able to keep in the setlist
         */
        private void ensureCapacity(int length) {
            if (length <= setList.length) {
                return;
            }
    
            setList = Arrays.copyOf(setList, length);
        }
  • §

    getSetList()

  • §

    This is defensive programming, because objects are passed by reference in Java, meaning if we just returned our setList array, outside code could directly manipulate this array (because they hold a reference).

    If we create a new array, but do not create new Track objects, other code could change the tracks within our setList, because they have references to our track objects.

    This style of programming is only necessary if we don’t trust the code that uses our methods. Sometimes it can be useful to directly manipulate the data structures contained within a class.

        /**
         * gets the setlist
         * <p>
         * This method returns a defensive copy, meaning it returns a copy of the setlist, which contains (deep) copies of
         * the tracks in the setlist. The returned array does not contain any null entries. If the setlist is empty an
         * array of length 0 is returned.
         *
         * @return the setlist of this concert
         */
        public Track[] getSetList() {
            Track[] tracks = new Track[nextIdx];
    
            for (int i = 0; i < nextIdx; i++) {
                tracks[i] = new Track(setList[i]);
            }
    
            return tracks;
        }
  • §

    setSetList()

  • §
        /**
         * sets the setList
         * <p>
         * This method creates a defensive copy, meaning it sets the setlist of this concert to contain (deep copies of)
         * all non-null tracks of the argument (and only those) thereby preserving the relative ordering of entries.
         * Null entries in the argument are ignored and not part of the resulting setlist.
         * A null argument is generally ignored.
         *
         * @param tracks the tracks for the setlist
         */
        public void setSetList(Track[] tracks) {
            if (tracks == null) {
                return;
            }
    
            setList = new Track[tracks.length];
            nextIdx = 0;
    
            for (Track t : tracks) {
                if (t == null) {
                    continue;
                }
                addTrack(new Track(t));
            }
        }
  • §

    resetSetList()

  • §

    Instead of Arrays.fill we could create a new array here. But this way we keep the capacity we already have.

        /**
         * removes all tracks from the setlist
         */
        public void resetSetList() {
            nextIdx = 0;
            Arrays.fill(setList, null);
        }
    
        /**
         * get the length of the playlist the length of the playlist is the number of entries in the setlist.
         *
         * @return the number of tracks in the setlist
         */
        public int nrTracks() {
            return nextIdx;
        }
  • §

    duration()

  • §

    This is probably super confusing. Map and reduce are concepts from functional programming. The imperative implementation would be a for loop that adds the durations to a sum variable.

    For the curious:

    • map takes a function which transforms each incoming value to a new value, possibly of a different type. Track::getDuration has the type Track -> int, so takes a Track object and returns an integer.

    • reduce takes a function which combines two values from the stream/list and produces a new value. reduce will call that function for all values to generate one final value (the reduction).

      Integer::sum has the type (int, int) -> int.

        /**
         * calculates the total duration (in seconds) of all tracks in the setlist
         * <p>
         * More specifically the method returns an estimation (lower bound) since tracks with unknown duration are treated having duration 0.
         *
         * @return the total duration of the setlist in seconds
         */
        public int duration() {
            return Arrays.stream(getSetList()).map(Track::getDuration).reduce(Integer::sum).orElseGet(() -> 0);
        }
  • §

    impact()

  • §

    This is the overridden impact() method, which will be called by Event.toString.

        /**
         * returns the impact of this event
         * <p>
         * the impact is an estimation of the number of people who took notice of this event. For a concert,
         * the impact is calculated from the number of attendees and the length of the concert. The number of
         * attendees is multiplied by the duration factor, which is initially 1 but increases by one for every
         * started half hour the concert lasts.
         * E.G: 400 people attending the concert. 75 minutes duration; duration factor=3
         * (two full half hours, plus one started half hour) impact therefore is 400*3.
         *
         * @return the impact
         */
        public int impact() {
            int factor = 1 + (duration() / 1800);
            return getAttendees() * factor;
        }
  • §

    toString()

  • §

    And we extend toString for concerts, using the parent class (Event.toString) by calling super.toString().

        /**
         * returns a String representation of this concert
         * <p>
         * the string representation of a concert appends the following line to the string representation
         * of a generic event (without quotes):
         *
         * <pre>
         * "number of tracks" tracks played, total duration "time".
         * </pre>
         * <p>
         * time is displayed in the format hh:mm with leading zeros
         *
         * @return the string representation
         */
        public String toString() {
            int min = duration() / 60;
            String hours = String.format("%02d", (min / 60) % 100);
            String minutes = String.format("%02d", min % 60);
    
            return super.toString() + "\n" +
                    nrTracks() + " tracks played, total duration " + hours + ":" + minutes + ".";
        }
    }