Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

import junit.framework.TestCase; /**********************************************

ID: 3883829 • Letter: I

Question

import junit.framework.TestCase;

/*****************************************************************************

* A Song is a sequence of Note objects.

* The song can have a special "current element," which is specified and

* accessed through four methods that are available in the song class

* (start, getCurrent, advance and hasCurrent).

*

* @note

* (1) The capacity of a song can change after it's created, but

* the maximum capacity is limited by the amount of free memory on the

* machine. The constructor, insert, insertAll, clone,

* and catenation methods will result in an

* OutOfMemoryError when free memory is exhausted.

*

* (2) A song's capacity cannot exceed the maximum integer 2,147,483,647

* (Integer.MAX_VALUE). Any attempt to create a larger capacity

* results in a failure due to an arithmetic overflow.

*

* NB: Neither of these conditions require any work for the implementors (students).

*

*

******************************************************************************/

public class Song implements Cloneable {

   /** Static Constants */

   private static final String DEFAULT_NAME = "Untitled";

   private static final int DEFAULT_BPM = 60;

   private static final int INITIAL_CAPACITY = 1;

   public static final int MIN_BPM = 20, MAX_BPM = 1000;

   /** Fields */

   private String _name;

   private int _bpm;

   private Note[ ] _data;

   private int _manyItems;

   private int _currentIndex;

   // Invariant of the Song class:

   // 1. The name is not null.

   // 2. The bpm is in the range [MIN_BPM,MAX_BPM]

   // 3. The number of elements in the songs is in the instance variable

   // manyItems.

   // 4. For an empty song (with no elements), we do not care what is

   // stored in any of data; for a non-empty song, the elements of the

   // song are stored in data[0] through data[manyItems-1], and we

   // don't care what's in the rest of data.

   // 5. If there is a current element, then it lies in data[currentIndex];

   // if there is no current element, then currentIndex equals manyItems.

   private static boolean doReport = true; // change onluy in invariant tests

   private boolean _report(String error) {

       if (doReport) {

           System.out.println("Invariant error: " + error);

       }

       return false;

   }

   private boolean _wellFormed() {

       // Check the invariant.

      

       // 1. name is never null

       if (_name == null) return _report("name is null");

       // 2. _bpm is in valid range: [MIN_BPM, MAX_BPM]

       // TODO

       // 3. data array is never null

       // TODO

       // 4. The data array has at least as many items in it as manyItems

       // claims the song has

       // TODO

       // 5. currentIndex is never negative and never more than the number of

       // items in the song.

       // TODO  

       // If no problems discovered, return true

       return true;

   }

   // This is only for testing the invariant. Do not change!

   private Song(boolean testInvariant) { }

   /**

   * Initialize an empty song with name DEFAULT_NAME, bpm DEFAULT_BPM, and

   * an initial capacity of INITIAL_CAPACITY. The {@link #insert(Note)} method works

   * efficiently (without needing more memory) until this capacity is reached.

   * @param - none

   * @postcondition

   * This song has the default name and BPM, is empty and has an initial capacity of INITIAL_CAPACITY

   * @exception OutOfMemoryError

   * Indicates insufficient memory for initial array.

   **/

   public Song( )

   {

       // TODO: Implemented by student.

       assert _wellFormed() : "invariant failed at end of constructor";

   }

   /**

   * Initialize an empty song with a specified name and bpm, an initial

   * capacity of INITIAL_CAPACITY. The {@link #insert(Note)} method works

   * efficiently (without needing more memory) until this capacity is reached.

   * @param name

   * the name of this song, must not be null

   * @param bpm

   * the beats per minute of this song, must be in the range [MIN_BPM,MAX_BPM]

   * @postcondition

   * This song is empty, has specified name and bpm, and has an initial

   * capacity of INITIAL_CAPACITY.

   * @throws IllegalArgumentException

   * If the name is null, or the bpm is out of the legal range.

   * @exception OutOfMemoryError

   * Indicates insufficient memory for an array with this many elements.

   * new Note[initialCapacity].

   **/

   public Song(String name, int bpm)

   {

       // TODO: Implemented by student.

       assert _wellFormed() : "invariant failed at end of constructor";

   }

   /**

   * Initialize an empty song with a specified initial capacity.

   * The {@link #insert(Note)} method works

   * efficiently (without needing more memory) until this capacity is reached.

   * @param name

   * the name of this song, must not be null

   * @param bpm

   * the beats per minute of this song, must be in the range [MIN_BPM,MAX_BPM]

   * @param initialCapacity

   * the initial capacity of this song, must not be negative

   * @exception IllegalArgumentException

   * Indicates that name, bpm or initialCapacity are invalid

   * @exception OutOfMemoryError

   * Indicates insufficient memory for an array with this many elements.

   * new Note[initialCapacity].

   **/

   public Song(String name, int bpm, int initialCapacity)

   {

      

       // TODO: Implemented by student.

       assert _wellFormed() : "invariant failed at end of constructor";

   }

   /**

   * Gets the name of the song

   * @return the name

   */

   public String getName() {

       assert _wellFormed() : "invariant failed at start of getName";

       return _name;

   }

   /**

   * Gets the beats per minute of the song.

   * @return the bpm

   */

   public int getBPM() {

       assert _wellFormed() : "invariant failed at start of getBPM";

       return _bpm;

   }

   /**

   * Gets the total duration of the song by adding duration of all its notes.

   * @return the total duration

   */

   public double getDuration() {

       assert _wellFormed() : "invariant failed at start of getDuration";

       double result = 0;

       // TODO

       return result;

   }

   /**

   * Sets the name of the song.

   * @param newName the new name, must not be null

   */

   public void setName(String newName) {

       assert _wellFormed() : "invariant failed at start of setName";

       // TODO

       assert _wellFormed() : "invariant failed at end of setName";

   }

   /**

   * Sets the beats per minute (BPM) of the song.

   * @param newBPM the new bpm

   * @throws IllegalArgumentException in the new BPM is not in the range [MIN_BPM,MAX_BPM]

   */

   public void setBPM(int newBPM) {

       assert _wellFormed() : "invariant failed at start of setBPM";

       // TODO

       assert _wellFormed() : "invariant failed at end of setBPM";

   }

   /**

   * Stretches the song by the given factor, lengthening or shortening its duration.

   * If there's a problem with one of the notes, this Song may be left

   * partially stretched.

   * @param factor the factor to multiply each note's duration by

   * @throws IllegalArgumentException if song is transposed where a note's duration

   *                is beyond the valid bounds

   */

   public void stretch(double factor) {

       assert _wellFormed() : "invariant failed at start of stretch";

       // TODO stretch each note in the song

       assert _wellFormed() : "invariant failed at end of stretch";

   }

   /**

   * Transposes the song by the given interval, raising or lowering its pitch.

   * If there's a problem with transposing one of the notes, this Song may be left

   * partially transposed.

   * @param interval the interval to transpose each note in the song

   * @throws IllegalArgumentException if song is transposed where a note is beyond the bounds

   *                                    of valid MIDI pitch values [0,127]

   */

   public void transpose(int interval) {

       assert _wellFormed() : "invariant failed at start of transpose";

       // TODO transpose each note in the song

       assert _wellFormed() : "invariant failed at end of transpose";

   }

   /**

   * Add a new element to this song, before the current element (if any).

   * If the new element would take this song beyond its current capacity,

   * then the capacity is increased before adding the new element.

   * @param element

   * the new element that is being added

   * @postcondition

   * A new copy of the element has been added to this song. If there was

   * a current element, then the new element is placed before the current

   * element. If there was no current element, then the new element is placed

   * at the end of the song. In all cases, the new element becomes the

   * new current element of this song.

   * @exception OutOfMemoryError

   * Indicates insufficient memory for increasing the song's capacity.

   **/

   public void insert(Note element)

   {

       assert _wellFormed() : "invariant failed at start of insert";

       // TODO: Implemented by student.

       assert _wellFormed() : "invariant failed at end of insert";

   }

   /**

   * Move forward, so that the current element is now the next element in

   * this song.

   * @param - none

   * @precondition

   * hasCurrent() returns true.

   * @postcondition

   * If the current element was already the end element of this song

   * (with nothing after it), then there is no longer any current element.

   * Otherwise, the new element is the element immediately after the

   * original current element.

   * @exception IllegalStateException

   * Indicates that there is no current element, so

   * advance may not be called.

   **/

   public void advance( )

   {

       assert _wellFormed() : "invariant failed at start of advance";

       // TODO: Implemented by student.

       assert _wellFormed() : "invariant failed at end of advance";

   }

   /**

   * Generate a copy of this song.

   * @param - none

   * @return

   * The return value is a copy of this song. Subsequent changes to the

   * copy will not affect the original, nor vice versa.

   * @exception OutOfMemoryError

   * Indicates insufficient memory for creating the clone.

   **/

   public Song clone( ) {

       assert _wellFormed() : "invariant failed at start of clone";

       Song answer;

       try

       {

           answer = (Song) super.clone( );

       }

       catch (CloneNotSupportedException e)

       { // This exception should not occur. But if it does, it would probably

           // indicate a programming error that made super.clone unavailable.

           // The most common error would be forgetting the "Implements Cloneable"

           // clause at the start of this class.

           throw new RuntimeException

           ("This class does not implement Cloneable");

       }

       // all that is needed is to clone the data array.

       // (Exercise: Why is this needed?)

       answer._data = _data.clone( );

       assert _wellFormed() : "invariant failed at end of clone";

       assert answer._wellFormed() : "invariant on answer failed at end of clone";

       return answer;

   }

   /**

   * Place the contents of another song (which may be the

   * same song as this!) into this song before the current element.

   * @param addend

   * a song whose contents will be placed into this song

   * @precondition

   * The parameter, addend, is not null.

   * @postcondition

   * The elements from addend have been placed into

   * this song. The current element of this song is now

   * the first element inserted (if any). If the added song

   * is empty, this song and the current element (if any) are

   * unchanged.

   * @exception NullPointerException

   * Indicates that addend is null.

   * @exception OutOfMemoryError

   * Indicates insufficient memory to increase the size of this song.

   **/

   public void insertAll(Song addend)

   {

       assert _wellFormed() : "invariant failed at start of insertAll";

       // TODO: Implemented by student.

       // Watch out for the this==addend case!

       // (It is possible to write code that works for this case AND

       // the normal case, but you have to be very careful.)

       assert _wellFormed() : "invariant failed at end of insertAll";

       assert addend._wellFormed() : "invariant of addend broken in insertAll";

   }

   /**

   * Create a new song that contains all the elements from one song

   * followed by another. The new BPM is the average of the two songs,

   * and the name is the concatenation of the two names separated by " and "

   * @param s1

   * the first of two songs

   * @param s2

   * the second of two songs

   * @precondition

   * Neither s1 nor s2 is null.

   * @return

   * a new song that has the elements of s1 followed by the

   * elements of s2 (with no current element).

   * @exception NullPointerException.

   * Indicates that one of the arguments is null.

   * @exception OutOfMemoryError

   * Indicates insufficient memory for the new song.

   **/

   public static Song catenation(Song s1, Song s2)

   {

       assert s1._wellFormed() : "invariant of s1 failed at start of catenation";

       assert s2._wellFormed() : "invariant of s2 failed at start of catenation";

       Song res;

       // TODO: Implemented by student.

       assert s1._wellFormed() : "invariant of s1 failed at end of catenation";

       assert s2._wellFormed() : "invariant of s2 failed at end of catenation";

       assert res._wellFormed() : "invariant of res failed at end of catenation";

       return res;

   }

   /**

   * Change the current capacity of this song as needed so that

   * the capacity is at least as big as the parameter.

   * This code must work correctly and efficiently if the minimum

   * capacity is (1) smaller or equal to the current capacity (do nothing)

   * (2) at most double the current capacity (double the capacity)

   * or (3) more than double the current capacity (new capacity is the

   * minimum passed).

   * @param minimumCapacity

   * the new capacity for this song

   * @postcondition

   * This song's capacity has been changed to at least minimumCapacity.

   * If the capacity was already at or greater than minimumCapacity,

   * then the capacity is left unchanged.

   * @exception OutOfMemoryError

   * Indicates insufficient memory for: new array of minimumCapacity elements.

   **/

   private void ensureCapacity(int minimumCapacity)

   {

       // TODO: Implemented by student.

       // NB: do not check invariant

   }

   /**

   * Accessor method to get the current element of this song.

   * @param - none

   * @precondition

   * hasCurrent() returns true.

   * @return

   * the current element of this song

   * @exception IllegalStateException

   * Indicates that there is no current element, so

   * getCurrent may not be called.

   **/

   public Note getCurrent( )

   {

       assert _wellFormed() : "invariant failed at start of getCurrent";

       // TODO: Implemented by student.

       // Don't change "this"!

   }

   /**

   * Accessor method to determine whether this song has a specified

   * current element that can be retrieved with the

   * getCurrent method.

   * @param - none

   * @return

   * true (there is a current element) or false (there is no current element at the moment)

   **/

   public boolean hasCurrent( )

   {

       assert _wellFormed() : "invariant failed at start of hasCurrent";

       // TODO: Implemented by student.

   }

   /**

   * Remove the current element from this song.

   * @param - none

   * @precondition

   * hasCurrent() returns true.

   * @postcondition

   * The current element has been removed from this song, and the

   * following element (if there is one) is now the new current element.

   * If there was no following element, then there is now no current

   * element.

   * @exception IllegalStateException

   * Indicates that there is no current element, so

   * removeCurrent may not be called.

   **/

   public void removeCurrent( )

   {

       assert _wellFormed() : "invariant failed at start of removeCurrent";

       // TODO: Implemented by student.

       assert _wellFormed() : "invariant failed at end of removeCurrent";

   }

   /**

   * Determine the number of elements in this song.

   * @param - none

   * @return

   * the number of elements in this song

   **/

   public int size( )

   {

       assert _wellFormed() : "invariant failed at start of size";

       // TODO: Implemented by student.

   }

   /**

   * Set the current element at the front of this song.

   * @param - none

   * @postcondition

   * The front element of this song is now the current element (but

   * if this song has no elements at all, then there is no current

   * element).

   **/

   public void start( )

   {

       assert _wellFormed() : "invariant failed at start of start";

       // TODO: Implemented by student.

       assert _wellFormed() : "invariant failed at end of start";

   }

/**

* The Immutable Class Note.

*/

public class Note {

   /** Static Constants */

   public static final int DEFAULT_INTENSITY = 50;

   public static final int REST_PITCH = 128; // First illegal pitch, used for rests.

   private static final int PITCHES_PER_OCTAVE = 12;

   private static final String[] NOTE_LETTERS = {"c","c#","d","d#","e","f","f#","g","g#","a","a#","b"};

   private static final double MIN_DURATION = 1.0/64, // One sixty-fourth

           MAX_DURATION = 8.0; // Eight whole notes

   /** Fields (Immutable) */

   private final String pitch;

   private final int midiValue;

   private final double duration;

   /**

   * Instantiates a new note based on a string denoting note letter and octave.

   *

   * @param pitch the pitch (e.g. "f6")

   * @param duration the duration

   * @throws NullPointerException if pitch is null

   * @throws IllegalArgumentException if:

   *                1. The pitch parameter is malformed or out of range.

   *                2. The duration parameter is out of range.

   */

   public Note(String pitch, double duration) {

       // #(

       this(toMidi(pitch), duration);

       // #)

       // Recommended: First implement toMidi(String).

   }

   /**

   * Instantiates a new note based on MIDI value.

   *

   * @param midiValue the MIDI value (e.g. 68)

   * @param duration the duration

   * @throws IllegalArgumentException if:

   *                1. The MIDI pitch parameter is out of range.

   *                2. The duration parameter is out of range.

   */

   public Note(int midiValue, double duration) {

       // #(

       String pitch = toPitch(midiValue);

       if (pitch == null)

           throw new IllegalArgumentException("Invalid MIDI value: " + midiValue + " -- Must be [0, 128]");

       if (duration < MIN_DURATION || duration > MAX_DURATION)

           throw new IllegalArgumentException("Invalid note duration: " + duration+" -- Must be [1/64, 8]");

       this.pitch = pitch;

       this.midiValue = midiValue;

       this.duration = duration;

       // #)

  

       // Recommended: First implement toPitch(int).

   }

   /**

   * Instantiates a new note from a String matching the format of Note's toString() method.

   *

   * @param note the string representation

   *

   * @throws IndexOutOfBoundsException if parameter isn't in correct format

   * @throws NumberFormatException if duration representation cannot be parsed as double

   * @throws IllegalArgumentException if the elements in the format are not permitted.

   */

   public Note(String note) {

       this(note.split(" x ")[0], Double.parseDouble(note.split(" x ")[1]));

   }

   /**

   * Converts a pitch string to a MIDI value.

   * The pitch "rest" should return {@link #REST_PITCH}.

   *

   * @param pitch the pitch to convert

   * @throws NullPointerException if pitch is null

   * @throws IllegalArgumentException is the String is not a legal pitch

   * @return the MIDI value

   */

   public static int toMidi(String pitch) {

       // #(

       int result;

       if (pitch.equals("rest")) // force NPE

           result = REST_PITCH;

       else {

           try {

               String[] tokens = pitch.split("(?=\d)", 2);

               findNote: if (tokens.length == 2) {

                   int octave = Integer.parseInt(tokens[1]);

                   for (int i = 0; i < NOTE_LETTERS.length; i++) {

                       if (NOTE_LETTERS[i].equals(tokens[0])) {

                           // i is the offset from the octave's "c" note

                           result = i + PITCHES_PER_OCTAVE * octave;

                           if (result < 0 || result >= REST_PITCH)

                               throw new IllegalArgumentException("pitch out of legal range.");

                           break findNote;

                       }

                   }

                   throw new IllegalArgumentException("unknown note: '" + tokens[0] + "'");

               } else {

                   throw new IllegalArgumentException("can't parse '" + pitch + "'");

               }

           } catch (IllegalArgumentException ex) {

               throw ex;

           } catch (Exception e) {

               throw new IllegalArgumentException(e);

           }

       }

       return result;

       /* #)

       return -1;

       ## */

   }

   /**

   * Converts a MIDI value to a pitch string.

   * The MIDI value 128 should return "rest".

   *

   * @param midiValue the MIDI value to convert

   * @throws IllegalArgumentException if the MIDI value is outside of legal range

   * @return the pitch string

   */

   public static String toPitch(int midiValue) {

       // #(

       String result;

       if (midiValue >= 0 && midiValue < REST_PITCH) {

           int noteIndex = midiValue % PITCHES_PER_OCTAVE;

           int octave = midiValue / PITCHES_PER_OCTAVE;

           result = NOTE_LETTERS[noteIndex] + (int) octave;

       }

       else if (midiValue == REST_PITCH)

           result = "rest";

       else throw new IllegalArgumentException("pitch out of range");

       return result;

       /* #)

  

       return null;

       ## */

   }

   /**

   * Gets the pitch string of this note.

   *

   * @return the pitch

   */

   public String getPitch() { return pitch; }

   /**

   * Gets the MIDI value of this note.

   *

   * @return the MIDI value

   */

   public int getMidiPitch() { return midiValue; }

   /**

   * Gets the duration of this note.

   *

   * @return the duration

   */

   public double getDuration() { return duration; }

   /**

   * Returns a new note with the same pitch, but with its duration multiplied by the parameter.

   *

   * @param factor the amount to scale by

   * @throws IllegalArgumentException if resultant duration is outside of valid range

   * @return the stretched note

   */

   public Note stretch(double factor) {

       // #(

       return new Note(pitch, duration * factor);

       /* #)

  

       return null;

       ## */

   }

   /**

   * Returns a (new) note with the same duration, but transposed by the given interval.

   *

   * @param interval the interval to transpose by

   * @throws IllegalArgumentException if note is transposed beyond valid bounds [c0, g10]

   * @return the transposed note

   */

   public Note transpose(int interval) {

       // #(

       Note result = null;

       if (midiValue == REST_PITCH)

           result = this;

       else {

           int newMidiValue = midiValue + interval;

           if (newMidiValue == REST_PITCH)

               newMidiValue++;

           result = new Note(newMidiValue, duration);

       }

       return result;

       /* #)

  

       return null;

       ## */

   }

   /**

   * Returns a string representation of this Note.

   * It should follow the format found in songs/InMyLife.song, namely:

   *    For a Note with pitch "g#4" and duration 1.0625 -> "g#4 x 1.0625"

   * NB1: Identical spacing and format are important!

   * NB2: For a "rest" note, the same format must be used (including duration).

   *

   * @return the string representation

   */

   @Override

   public String toString() {

       // #(

       return pitch + " x " + duration;

       /* #)

       return null;

       ## */

   }

   /* (non-Javadoc)

   * @see java.lang.Object#equals(java.lang.Object)

   */

   @Override

   public boolean equals(Object o) {

       // #(

       if (o instanceof Note) {

           Note other = (Note) o;

           return this.pitch.equals(other.pitch) &&

                   this.midiValue == other.midiValue &&

                   this.duration == other.duration;

       }

       // #)

       // Return equal if the argument is a Note and the midiValue and duration are equal

       return false;

   }

   @Override

   public int hashCode() {

       // #(

       long durHash = Double.doubleToLongBits(duration);

       durHash = durHash ^ (durHash >> 32);

       return (int) (durHash + midiValue);

       /* #)

       // Compute hash using pieces. (Don't take hash code of strings.)

       return -1;

       ## */

   }

}

Explanation / Answer

import java.nio.channels.IllegalSelectorException;

import junit.framework.TestCase;

/*****************************************************************************

* A Song is a sequence of Note objects.

* The song can have a special "current element," which is specified and

* accessed through four methods that are available in the song class

* (start, getCurrent, advance and hasCurrent).

*

* @note

* (1) The capacity of a song can change after it's created, but

* the maximum capacity is limited by the amount of free memory on the

* machine. The constructor, insert, insertAll, clone,

* and catenation methods will result in an

* OutOfMemoryError when free memory is exhausted.

*

* (2) A song's capacity cannot exceed the maximum integer 2,147,483,647

* (Integer.MAX_VALUE). Any attempt to create a larger capacity

* results in a failure due to an arithmetic overflow.

*

* NB: Neither of these conditions require any work for the implementors (students).

*

*

******************************************************************************/

public class Song implements Cloneable {

/** Static Constants */

private static final String DEFAULT_NAME = "Untitled";

private static final int DEFAULT_BPM = 60;

private static final int INITIAL_CAPACITY = 1;

public static final int MIN_BPM = 20, MAX_BPM = 1000;

/** Fields */

private String _name;

private int _bpm;

private Note[ ] _data;

private int _manyItems;

private int _currentIndex;

// Invariant of the Song class:

// 1. The name is not null.

// 2. The bpm is in the range [MIN_BPM,MAX_BPM]

// 3. The number of elements in the songs is in the instance variable

// manyItems.

// 4. For an empty song (with no elements), we do not care what is

// stored in any of data; for a non-empty song, the elements of the

// song are stored in data[0] through data[manyItems-1], and we

// don't care what's in the rest of data.

// 5. If there is a current element, then it lies in data[currentIndex];

// if there is no current element, then currentIndex equals manyItems.

private static boolean doReport = true; // change onluy in invariant tests

private boolean _report(String error) {

if (doReport) {

System.out.println("Invariant error: " + error);

}

return false;

}

private boolean _wellFormed() {

// Check the invariant.

// 1. name is never null

if (_name == null) return _report("name is null");

// 2. _bpm is in valid range: [MIN_BPM, MAX_BPM]

// TODO

if(_bpm<MIN_BPM || _bpm>MAX_BPM) return _report("bpm is invalid");

// 3. data array is never null

// TODO

if(_data==null) return _report("data array is null");

// 4. The data array has at least as many items in it as manyItems

// claims the song has

// TODO

if(_data.length<_manyItems) return _report("data array does not have sufficient items");

// 5. currentIndex is never negative and never more than the number of

// items in the song.

// TODO

if(_currentIndex<0 || _currentIndex>_manyItems) return _report("current indes is negative or greater than number of items in the song");

// If no problems discovered, return true

return true;

}

// This is only for testing the invariant. Do not change!

private Song(boolean testInvariant) { }

/**

* Initialize an empty song with name DEFAULT_NAME, bpm DEFAULT_BPM, and

* an initial capacity of INITIAL_CAPACITY. The {@link #insert(Note)} method works

* efficiently (without needing more memory) until this capacity is reached.

* @param - none

* @postcondition

* This song has the default name and BPM, is empty and has an initial capacity of INITIAL_CAPACITY

* @exception OutOfMemoryError

* Indicates insufficient memory for initial array.

**/

public Song( )

{

// TODO: Implemented by student.

_name=DEFAULT_NAME;

_bpm=DEFAULT_BPM;

_manyItems=INITIAL_CAPACITY;

_data=new Note[_manyItems];

assert _wellFormed() : "invariant failed at end of constructor";

}

/**

* Initialize an empty song with a specified name and bpm, an initial

* capacity of INITIAL_CAPACITY. The {@link #insert(Note)} method works

* efficiently (without needing more memory) until this capacity is reached.

* @param name

* the name of this song, must not be null

* @param bpm

* the beats per minute of this song, must be in the range [MIN_BPM,MAX_BPM]

* @postcondition

* This song is empty, has specified name and bpm, and has an initial

* capacity of INITIAL_CAPACITY.

* @throws IllegalArgumentException

* If the name is null, or the bpm is out of the legal range.

* @exception OutOfMemoryError

* Indicates insufficient memory for an array with this many elements.

* new Note[initialCapacity].

**/

public Song(String name, int bpm)

{

// TODO: Implemented by student.

if(name==null)

{

throw new IllegalArgumentException("name must not be null");

}

_name=name;

_bpm=bpm;

_manyItems=INITIAL_CAPACITY;

_data=new Note[_manyItems];

assert _wellFormed() : "invariant failed at end of constructor";

}

/**

* Initialize an empty song with a specified initial capacity.

* The {@link #insert(Note)} method works

* efficiently (without needing more memory) until this capacity is reached.

* @param name

* the name of this song, must not be null

* @param bpm

* the beats per minute of this song, must be in the range [MIN_BPM,MAX_BPM]

* @param initialCapacity

* the initial capacity of this song, must not be negative

* @exception IllegalArgumentException

* Indicates that name, bpm or initialCapacity are invalid

* @exception OutOfMemoryError

* Indicates insufficient memory for an array with this many elements.

* new Note[initialCapacity].

**/

public Song(String name, int bpm, int initialCapacity)

{

if(name==null || bpm<MIN_BPM || bpm>MAX_BPM || initialCapacity<0)

{

throw new IllegalArgumentException("Invalid parameters");

}

_name=name;

_bpm=bpm;

_manyItems=initialCapacity;

_data=new Note[_manyItems];

// TODO: Implemented by student.

assert _wellFormed() : "invariant failed at end of constructor";

}

/**

* Gets the name of the song

* @return the name

*/

public String getName() {

assert _wellFormed() : "invariant failed at start of getName";

return _name;

}

/**

* Gets the beats per minute of the song.

* @return the bpm

*/

public int getBPM() {

assert _wellFormed() : "invariant failed at start of getBPM";

return _bpm;

}

/**

* Gets the total duration of the song by adding duration of all its notes.

* @return the total duration

*/

public double getDuration() {

assert _wellFormed() : "invariant failed at start of getDuration";

double result = 0;

// TODO

for(int i=0;i<_manyItems;i++)

{

result=result+_data[i].getDuration();

}

return result;

}

/**

* Sets the name of the song.

* @param newName the new name, must not be null

*/

public void setName(String newName) {

assert _wellFormed() : "invariant failed at start of setName";

// TODO

_name=newName;

assert _wellFormed() : "invariant failed at end of setName";

}

/**

* Sets the beats per minute (BPM) of the song.

* @param newBPM the new bpm

* @throws IllegalArgumentException in the new BPM is not in the range [MIN_BPM,MAX_BPM]

*/

public void setBPM(int newBPM) {

assert _wellFormed() : "invariant failed at start of setBPM";

// TODO

if(newBPM<MIN_BPM || newBPM>MAX_BPM )

{

throw new IllegalArgumentException("Invalid bpm");

}

assert _wellFormed() : "invariant failed at end of setBPM";

}

/**

* Stretches the song by the given factor, lengthening or shortening its duration.

* If there's a problem with one of the notes, this Song may be left

* partially stretched.

* @param factor the factor to multiply each note's duration by

* @throws IllegalArgumentException if song is transposed where a note's duration

* is beyond the valid bounds

*/

public void stretch(double factor) {

assert _wellFormed() : "invariant failed at start of stretch";

// TODO stretch each note in the song

try

{

for(int i=0;i<_manyItems;i++)

{

_data[i]=_data[i].stretch(factor);

}

}

catch(Exception e)

{

System.out.println(e.getMessage());

}

assert _wellFormed() : "invariant failed at end of stretch";

}

/**

* Transposes the song by the given interval, raising or lowering its pitch.

* If there's a problem with transposing one of the notes, this Song may be left

* partially transposed.

* @param interval the interval to transpose each note in the song

* @throws IllegalArgumentException if song is transposed where a note is beyond the bounds

* of valid MIDI pitch values [0,127]

*/

public void transpose(int interval) {

assert _wellFormed() : "invariant failed at start of transpose";

// TODO transpose each note in the song

try

{

for(int i=0;i<_manyItems;i++)

{

_data[i]=_data[i].transpose(interval);

}

}

catch(Exception e)

{

System.out.println(e.getMessage());

}

assert _wellFormed() : "invariant failed at end of transpose";

}

/**

* Add a new element to this song, before the current element (if any).

* If the new element would take this song beyond its current capacity,

* then the capacity is increased before adding the new element.

* @param element

* the new element that is being added

* @postcondition

* A new copy of the element has been added to this song. If there was

* a current element, then the new element is placed before the current

* element. If there was no current element, then the new element is placed

* at the end of the song. In all cases, the new element becomes the

* new current element of this song.

* @exception OutOfMemoryError

* Indicates insufficient memory for increasing the song's capacity.

**/

public void insert(Note element)

{

assert _wellFormed() : "invariant failed at start of insert";

// TODO: Implemented by student.

_manyItems=_manyItems+1;

if(_data.length<_manyItems)

{

Note[] d=new Note[_data.length*2];

System.arraycopy(_data, 0, d, 0, _data.length);

_data=d;

}

if(hasCurrent())

{

_data[_currentIndex-1]=element;

}

else

{

_data[_manyItems-1]=element;

}

assert _wellFormed() : "invariant failed at end of insert";

}

/**

* Move forward, so that the current element is now the next element in

* this song.

* @param - none

* @precondition

* hasCurrent() returns true.

* @postcondition

* If the current element was already the end element of this song

* (with nothing after it), then there is no longer any current element.

* Otherwise, the new element is the element immediately after the

* original current element.

* @exception IllegalStateException

* Indicates that there is no current element, so

* advance may not be called.

**/

public void advance( )

{

assert _wellFormed() : "invariant failed at start of advance";

// TODO: Implemented by student.

if(hasCurrent())

{

_currentIndex++;

}

else

{

throw new IllegalStateException(" there is no current element");

}

assert _wellFormed() : "invariant failed at end of advance";

}

/**

* Generate a copy of this song.

* @param - none

* @return

* The return value is a copy of this song. Subsequent changes to the

* copy will not affect the original, nor vice versa.

* @exception OutOfMemoryError

* Indicates insufficient memory for creating the clone.

**/

public Song clone( ) {

assert _wellFormed() : "invariant failed at start of clone";

Song answer;

try

{

answer = (Song) super.clone( );

}

catch (CloneNotSupportedException e)

{ // This exception should not occur. But if it does, it would probably

// indicate a programming error that made super.clone unavailable.

// The most common error would be forgetting the "Implements Cloneable"

// clause at the start of this class.

throw new RuntimeException

("This class does not implement Cloneable");

}

// all that is needed is to clone the data array.

// (Exercise: Why is this needed?)

answer._data = _data.clone( );

assert _wellFormed() : "invariant failed at end of clone";

assert answer._wellFormed() : "invariant on answer failed at end of clone";

return answer;

}

/**

* Place the contents of another song (which may be the

* same song as this!) into this song before the current element.

* @param addend

* a song whose contents will be placed into this song

* @precondition

* The parameter, addend, is not null.

* @postcondition

* The elements from addend have been placed into

* this song. The current element of this song is now

* the first element inserted (if any). If the added song

* is empty, this song and the current element (if any) are

* unchanged.

* @exception NullPointerException

* Indicates that addend is null.

* @exception OutOfMemoryError

* Indicates insufficient memory to increase the size of this song.

**/

public void insertAll(Song addend)

{

assert _wellFormed() : "invariant failed at start of insertAll";

// TODO: Implemented by student.

// Watch out for the this==addend case!

// (It is possible to write code that works for this case AND

// the normal case, but you have to be very careful.)

if(addend==null)

{

throw new NullPointerException("song is null");

}

for(int i=0;i<addend._manyItems;i++)

{

this.insert(addend._data[i]);

}

assert _wellFormed() : "invariant failed at end of insertAll";

assert addend._wellFormed() : "invariant of addend broken in insertAll";

}

/**

* Create a new song that contains all the elements from one song

* followed by another. The new BPM is the average of the two songs,

* and the name is the concatenation of the two names separated by " and "

* @param s1

* the first of two songs

* @param s2

* the second of two songs

* @precondition

* Neither s1 nor s2 is null.

* @return

* a new song that has the elements of s1 followed by the

* elements of s2 (with no current element).

* @exception NullPointerException.

* Indicates that one of the arguments is null.

* @exception OutOfMemoryError

* Indicates insufficient memory for the new song.

**/

public static Song catenation(Song s1, Song s2)

{

assert s1._wellFormed() : "invariant of s1 failed at start of catenation";

assert s2._wellFormed() : "invariant of s2 failed at start of catenation";

Song res = null;

// TODO: Implemented by student.

if(s1==null || s2==null)

{

throw new NullPointerException("songs are null");

}

res=new Song(s1+" and "+s2,(s1.getBPM()+s2.getBPM())/2,s1._manyItems+s2._manyItems);

res.insertAll(s1);

res.insertAll(s2);

assert s1._wellFormed() : "invariant of s1 failed at end of catenation";

assert s2._wellFormed() : "invariant of s2 failed at end of catenation";

assert res._wellFormed() : "invariant of res failed at end of catenation";

return res;

}

/**

* Change the current capacity of this song as needed so that

* the capacity is at least as big as the parameter.

* This code must work correctly and efficiently if the minimum

* capacity is (1) smaller or equal to the current capacity (do nothing)

* (2) at most double the current capacity (double the capacity)

* or (3) more than double the current capacity (new capacity is the

* minimum passed).

* @param minimumCapacity

* the new capacity for this song

* @postcondition

* This song's capacity has been changed to at least minimumCapacity.

* If the capacity was already at or greater than minimumCapacity,

* then the capacity is left unchanged.

* @exception OutOfMemoryError

* Indicates insufficient memory for: new array of minimumCapacity elements.

**/

private void ensureCapacity(int minimumCapacity)

{

// TODO: Implemented by student.

// NB: do not check invariant

if(minimumCapacity>_data.length && minimumCapacity<=_data.length*2)

{

Note[] d=new Note[_data.length*2];

System.arraycopy(_data, 0, d, 0, _data.length);

_data=d;

}

else if(minimumCapacity>_data.length*2)

{

Note[] d=new Note[_data.length*2];

System.arraycopy(_data, 0, d, 0,minimumCapacity);

_data=d;

}

}

/**

* Accessor method to get the current element of this song.

* @param - none

* @precondition

* hasCurrent() returns true.

* @return

* the current element of this song

* @exception IllegalStateException

* Indicates that there is no current element, so

* getCurrent may not be called.

**/

public Note getCurrent( )

{

assert _wellFormed() : "invariant failed at start of getCurrent";

if(hasCurrent())

{

return _data[_currentIndex];

}

else

{

throw new IllegalStateException("no current element ");

}

// TODO: Implemented by student.

// Don't change "this"!

}

/**

* Accessor method to determine whether this song has a specified

* current element that can be retrieved with the

* getCurrent method.

* @param - none

* @return

* true (there is a current element) or false (there is no current element at the moment)

**/

public boolean hasCurrent( )

{

// TODO: Implemented by student.

assert _wellFormed() : "invariant failed at start of hasCurrent";

try

{

if( getCurrent()!=null)

{

return true;

}

}

catch(IllegalStateException e)

{

return false;

}

return false;

}

/**

* Remove the current element from this song.

* @param - none

* @precondition

* hasCurrent() returns true.

* @postcondition

* The current element has been removed from this song, and the

* following element (if there is one) is now the new current element.

* If there was no following element, then there is now no current

* element.

* @exception IllegalStateException

* Indicates that there is no current element, so

* removeCurrent may not be called.

**/

public void removeCurrent( )

{

assert _wellFormed() : "invariant failed at start of removeCurrent";

// TODO: Implemented by student.

if(hasCurrent())

{

_data[_currentIndex]=null;

_manyItems--;

for(int i=_currentIndex;i<_manyItems;i++)

{

_data[i]=_data[i+1];

}

}

else

{

throw new IllegalStateException("no current element ");

}

assert _wellFormed() : "invariant failed at end of removeCurrent";

}

/**

* Determine the number of elements in this song.

* @param - none

* @return

* the number of elements in this song

**/

public int size( )

{

assert _wellFormed() : "invariant failed at start of size";

// TODO: Implemented by student.

return _manyItems;

}

/**

* Set the current element at the front of this song.

* @param - none

* @postcondition

* The front element of this song is now the current element (but

* if this song has no elements at all, then there is no current

* element).

**/

public void start( )

{

assert _wellFormed() : "invariant failed at start of start";

// TODO: Implemented by student.

if(_manyItems>=1)

{

_currentIndex=0;

}

assert _wellFormed() : "invariant failed at end of start";

}

}