Java Java Data Structures Efficiency! Custom Serialization

Sara Worth
Sara Worth
5,225 Points

I would like to fix the Karaoke program so that if a user enters an incorrect selection won't crash

I am working on my own to fix the Karaoke program so that if a user enters an incorrect selection, the program responds with " Please make a valid selection" and lets the user try again, instead of crashing the program and editing. I am not getting very far. Any suggestions on how to do this efficiently?

The user is supposed to enter a number of one of the options on the screen. Any letter entered, or combination of letters and numbers, or nothing, or a number that is higher than the amount of options results in an IOException and the program crashes.

This is my attempt so far:. I am trying to check against anything but numbers being entered, nothing being entered, or a number that is higher than the options available. However this does not work and the program still exits if one of these invalid selections is made.

private int promptForIndex(List<String> options) throws IOException {
        int counter = 1;
        int choice;

        for (String option : options) {
            System.out.printf("%d.) %s %n", counter, option);
            counter ++;
        }

        String optionAsString = mReader.readLine();
        while (optionAsString.matches("\\D") || 
                           optionAsString == null || 
                           integer.parseInt(optionAsString.trim()) > options.length)
                {
            System.out.println("Please make valid selection... ");
            optionAsString = mReader.readline();
        }
        choice = Integer.parseInt(optionAsString.trim());
        return choice -1;

    }

Here is the full file:

package com.teamtreehouse;
import com.teamtreehouse.model.Song;
import com.teamtreehouse.model.SongBook;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;


public class KaraokeMachine {
  private SongBook mSongBook;
  private BufferedReader mReader;
  private Queue<Song> mSongQueue;
  private Map<String, String> mMenu;

  public KaraokeMachine(SongBook songBook) {
    mSongBook = songBook;
    mReader = new BufferedReader(new InputStreamReader(System.in));
    mSongQueue = new ArrayDeque<Song>();
    mMenu = new HashMap<String, String>();
    mMenu.put("add", "Add a new song to the song book");
    mMenu.put("play", "Play next song in the queue");
    mMenu.put("choose", "choose a song to sing!");
    mMenu.put("quit", "Give up. Exit the program");
  }

  private String promptAction() throws IOException {
   System.out.printf("There are %d songs available and %d in the queue.  Your options are: %n",
                     mSongBook.getSongCount(), 
                     mSongQueue.size());
    for (Map.Entry<String, String> option : mMenu.entrySet()) {
      System.out.printf("%s - %s %n",
                        option.getKey(),
                        option.getValue());
    }
    System.out.print("What do you want to do: ");
    String choice = mReader.readLine();
    return choice.trim().toLowerCase();
  }

  public void run() {
    String choice = "";
    do {
      try {
        choice = promptAction();
        switch(choice) {
          case "add":
            Song song = promptNewSong();
            mSongBook.addSong(song);
            System.out.printf("%s added!  %n%n%n", song);
            break;
          case "play":
            playNext();
            break;
          case "choose":
            String artist = promptArtist();
          Song artistSong = promptSongForArtist(artist);
          mSongQueue.add(artistSong);
          System.out.printf("%s %n", artistSong);
          break;
          case "quit":
            System.out.println("Thanks for playing!");
            break;
          default:
            System.out.printf("Unknown choice: '%s'. Try again.  %n%n%n%n",
                              choice);
        }
      } catch(IOException ioe) {
        System.out.println("Problem with input");
        ioe.printStackTrace();
      } 
    } while(!choice.equals("quit"));    

  }

  private Song promptNewSong() throws IOException {
    System.out.print("Enter the artist's name:  ");
    String artist = mReader.readLine();
    System.out.print("Enter the title:  ");
    String title = mReader.readLine();
    System.out.print("Enter the video URL:  ");
    String videoUrl = mReader.readLine();
    return new Song(artist, title, videoUrl);

  }

  private String promptArtist() throws IOException {
    System.out.println("Available artists: ");
    List<String> artist = new ArrayList<>(mSongBook.getArtists());
    int index = promptForIndex(artist);
    return artist.get(index);
  }

  private Song promptSongForArtist(String artist) throws IOException {
    System.out.printf("Available songs by %s %n", artist);
    List<Song> songs = mSongBook.getSongsForArtist(artist);
    List<String> songTitles = new ArrayList<>();
    for (Song song : songs) {
      songTitles.add(song.getTitle()); 
    }
    int index = promptForIndex(songTitles);
    return songs.get(index);
  }

    private int promptForIndex(List<String> options) throws IOException {
        int counter = 1;
        int choice;

        for (String option : options) {
            System.out.printf("%d.) %s %n", counter, option);
            counter ++;
        }

        String optionAsString = mReader.readLine();
        while (optionAsString.matches("\\D") || 
                    optionAsString == null ||
                    integer.parseint(optionAsString.trim()) > options.length)

                    {
            System.out.println("Please make valid selection... ");
            optionAsString = mReader.readline();
        }
        choice = Integer.parseInt(optionAsString.trim());
        return choice -1;

    }


// The following is the code that we learned to write in the lessons, and it is the block I am trying to fix

/*  
  private int promptForIndex(List<String> options) throws IOException {
    int counter = 1;
    int choice;

    for (String option : options) {
      System.out.printf("%d.)  %s %n", counter, option);
      counter ++;
    }
        String optionAsString = mReader.readLine();
        choice = Integer.parseInt(optionAsString.trim());
        return choice - 1; 
    }                
  }

  */

  public void playNext() {
    Song song = mSongQueue.poll();
    if (song == null) {
      System.out.println("Sorry there are no songs in the queue. " + 
                         "Use *choose* from the menu to add more songs.");
    } else {
      System.out.printf("%n%n%n Open %s to hear %s by %s %n%n%n",
                        song.getVideoUrl(),
                        song.getTitle(),
                        song.getArtist());
    }
  }

}

1 Answer

Philip Gales
Philip Gales
15,191 Points

What part of the program prompts for a letter and not a number? i.e. what part are you trying to fix?

EDIT:

private int promptForIndex(List<String> options) {
    int choice = 0;
    boolean success = false;

    //loop through message until it is a success
    while (success == false) {
        int counter = 0; //<--------started at 0 to account for moving of counter
        for (String option : options) {
            counter++; //<---------------moved in front of display
            System.out.printf("%d.)  %s %n", counter, option);
        }
        System.out.print("Your choice:   ");

        // try catch to see if answer is an int. If it is then it goes through if-statement to confirm it is within range
        try {
            String optionAsString = mReader.readLine();
            choice = Integer.parseInt(optionAsString.trim());

            // if choice is larger than selesction then spit out error message
            if (choice > counter || choice < 0) {
                System.out.println("Invalid selection. Try again.");      
            } else {
                success = true;
            }
        } catch (Exception k) {
            System.out.println("Invalid selection. Try again.");
        } 
    }
    return choice - 1;
}

The out of bounds exception was from another part of the code. This was because a large number was valid, but returning the large number caused errors elsewhere. I added the if-statement to account for this.

I used a generic exception to catch everything else.

Sara Worth
Sara Worth
5,225 Points

I have added my code so it is easier to see what I am trying to do.

Philip Gales
Philip Gales
15,191 Points

Updated my answer. Let me know what I missed.

Sara Worth
Sara Worth
5,225 Points

No, it needs a number input only, and not a number that is higher than the number of options provided.

Thank you for the code. I will try it out and let you know if it works in my situation.

Sara Worth
Sara Worth
5,225 Points

//UPDATE

OK that seems to work GREAT for most wrong inputs. The only exception is if there is only one option and I type in "2", it crashes the program. strange...

Philip Gales
Philip Gales
15,191 Points

That was because the counter would increment after displaying the last option. I moved it in front of the display so it wouldn't increment after displaying all the options.

Sara Worth
Sara Worth
5,225 Points

That works! The only thing I changed was the line

if (choice > counter || choice <= 0) {

to account for someone entering 0 which led to a crash in the program.

Thank you so much for following through with this and solving the problem!! :)

Philip Gales
Philip Gales
15,191 Points

No worries. Be sure to mark my answers as 'best answer' as they give me more points and really help. I just realized I have answered most of your questions recently haha.