• DurationMatcher.java

  • §
    package MusicLandscape.util.matcher;
    
    import MusicLandscape.entities.Track;
    import MusicLandscape.util.MyMatcher;
  • §

    Matchers are more complicated than Comparators. The matches() method itself is quite simple, but the setPattern() code to parse the matcher configuration from a string is where the complexity comes in.

    /**
     * Encapsulates the concept of matching a track based on its duration.
     * <p>
     * This class is used to test whether given a given track's duration lies in a certain range, the range being the
     * pattern of this matcher.The pattern is a simple string consisting of the (white-space separated) lower and upper
     * bounds (inclusive) of the range of duration s (in seconds) accepted by this matcher.
     * <p>
     * More precisely, a valid pattern is a String that can be interpreted as either a single integer number
     * (leading and trailing whitespace are ignored, if present) which then represents the lower bound
     * or two integer numbers, separated by (any number of) whitespace, which then represent lower and upper bound.
     * <p>
     * The bounds are understood to be inclusive.
     *
     * @author Jonas Altrock (ew20b126@technikum-wien.at)
     * @version 1
     * @since ExerciseSheet05
     */
    public class DurationMatcher extends MyMatcher<Track> {
        /**
         * the lower bound of the accepted range.
         */
        private int lower;
        /**
         * the upper bound of the accepted range.
         */
        private int upper;
    
        /**
         * Creates a default duration matcher.<br>
         * By default, a matcher matches any duration, including unknown duration.
         */
        public DurationMatcher() {
            super("");
        }
    
        /**
         * Creates a duration matcher with a specified pattern.
         *
         * @param pat the pattern of this matcher
         */
        public DurationMatcher(String pat) {
            super(pat);
        }
    
        /**
         * A track matches if its duration is in the range accepted by this matcher.
         *
         * @param track the object to match
         * @return whether t matches the pattern of this matcher.
         */
        @Override
        public boolean matches(Track track) {
            return track.getDuration() >= lower && track.getDuration() <= upper;
        }
    
        /**
         * Sets the pattern of this matcher.
         * <p>
         * Interprets the argument as described in the class documentation. First sets the lower, then the upper bound.
         * The bounds specified are set if and only if at the time of setting they are actually lower (for the lower bound)
         * or higher (for the upper bound) than the other or at least equal to the other.
         *
         * @param pat the pattern to set
         */
        @Override
        public void setPattern(String pat) {
  • §

    First I split the incoming string into two parts.

    • \s is regular expression for any whitespace, the backspace \ needs to be doubled because Java strings can also contain escape codes that start with the backslash, like \n.
    • + is for one or more occurrences, so “at least one whitespace”.
            String[] parts = pat.trim().split("\\s+");
  • §

    Here we have our default values for the lower and upper bounds.

            lower = 0;
            upper = Integer.MAX_VALUE;
    
            if (parts.length < 1 || parts[0].isEmpty()) {
                return;
            }
    
            try {
  • §

    Using Integer.max is a short hand for checking if the parsed integer is smaller than 0 (i.e. negative).

                lower = Integer.max(Integer.parseInt(parts[0]), 0);
            } catch (NumberFormatException ignored) {}
    
            if (parts.length < 2) {
                return;
            }
    
            try {
  • §

    Same in the “other direction”: using Integer.min makes sure the value is below the maximum upper bound. This is a little pointless, because upper == Integer.MAX_VALUE , and parseInt will never return a value larger than that.

                int upperBound = Integer.min(Integer.parseInt(parts[1]), upper);
    
                if (upperBound >= lower) {
                    upper = upperBound;
                }
            } catch (NumberFormatException ignored) {}
        }
    
        /**
         * the valid pattern is <kbd>LOWER UPPER</kbd> separated by whitespace.
         *
         * @return the pattern
         */
        @Override
        public String getPattern() {
            return lower + " " + upper;
        }
    
        /**
         * the string representation is duration in range (RANGE)<br>
         * with range as described in getPattern
         *
         * @return a string representation of the object.
         */
        @Override
        public String toString() {
            return "duration in range (" + lower + " " + upper + ")";
        }
    }