0

I have a homework assignment to create a class with a looping menu to manage a queue of cars. We learned queues in our last class.

My menu works perfectly fine until it catches InputMismatchException or QueueEmptyException, after which it goes into endless loop, not even stopping at the userInput.nextInt();. It works when it catches QueueFullException, but not the others.

My code is:

import java.util.*;

public class CarQueueManagement {

    public static void main(String[] args) throws InputMismatchException, QueueFullException{
        ArrayQueue queue = new ArrayQueue(3);;
        Scanner userInput = new Scanner(System.in);
        int carNum;
        int choice = 0;

        queue.add(1);

        OUTER:
        while (true) {
            try{
                System.out.println("ΜΕΝΟΥ:\n\t1. Άφιξη αυτοκινήτου");
                System.out.println("\t2. Αναχώρηση αυτοκινήτου\n\t3. Κατάσταση ουράς\n\t4. Έξοδος");
                System.out.print("\n\tΕπιλογή (1-4): ");
                choice = userInput.nextInt();

                switch (choice){
                    case 1:
                        System.out.print("\n\tΆφιξη αυτοκινήτου:\n\t\tΑριθμός Αμαξιού");
                        carNum = userInput.nextInt();
                        queue.add(carNum);
                        break;
                    case 2:
                        if(queue.isEmpty()){
                            System.out.println("\n\tΗ ουρά είναι άδεια, δεν χριάζεται διαγραφή.\n\n");
                            break;
                       }
                       String answer;
                        while(true){
                            System.out.print("\n\tΑναχώρηση αυτοκινήτου\n\t\tΕπιβεβαίωση; (y/n): ");
                            answer = userInput.next();
                            if(answer.equals("y")){
                                queue.remove();
                                break;
                            }
                            else if(answer.equals("n"))
                            break;
                        }
                        break;
                    case 3:
                        System.out.println("\n\tΚατάσταση ουράς:");
                        if(queue.isEmpty()) System.out.println("\t\tΗ ουρά είναι άδεια.\n\n");
                        else if(queue.isFull()) System.out.println("\t\tΗ ουρά είναι γεμάτη.\n\n");
                        else System.out.println("\t\tΗ ουρά έχει άδιες θέσοις.\n\n");
                        break;
                    case 4:
                        System.out.print("\n\nΕξοδος");
                        break OUTER;
                    default:
                        break;
                }
            }catch (InputMismatchException exc){
                System.out.println("\t\tΛΑΘΟΣ ΕΙΣΑΓΩΓΗ\n");  
            }catch(QueueEmptyException exc){
                System.out.println("\t\t" + exc.getMessage() + "\n");
            }catch(QueueFullException exc){
                System.out.println("\t\t" + exc.getMessage() + "\n");
            }
        }    
    }
}
Kevin J. Chase
  • 3,856
  • 4
  • 21
  • 43
  • do you know what is `while (true)` doing? – SMA Oct 29 '16 at 11:52
  • i know that until i put a break; point it loops, but i also know that when i have a input code it needs to stop to take the input. It works with `QueueFullException` but not with the others. The other 2 exception are looping through everything without even stopping at the `userInput.nextInt();` – Niyazi Hacihalil Oct 29 '16 at 11:55
  • I bet you are pressing return key after entering a number? – SMA Oct 29 '16 at 11:56
  • Possible duplicate of [Java InputMismatchException](http://stackoverflow.com/questions/16816250/java-inputmismatchexception) – qwerty1423 Oct 29 '16 at 11:57
  • i press `Enter` i use NetBeans and i test my code with the implemented `Run` option, as you can tell i only want my code to exit the loop and terminate the program wih selection 4 witch say's 'Exit' in greek. – Niyazi Hacihalil Oct 29 '16 at 11:58
  • It’s seriously off-topic, but my Greek is so poor: is that άδιες or άδειες θέσοις? – Ole V.V. Oct 29 '16 at 12:38
  • it's "άδειες θέσεις" even i got it wrong on "θέσοις" – Niyazi Hacihalil Oct 29 '16 at 13:06
  • What did you enter to trigger the `QueueEmptyException` endless loop? I was not able to reproduce the endless loop in Menu 2 (removing items from the queue), except by accident: I entered `y` to remove an entry one time too many, so the `y` was fed to the main menu loop, triggering the `InputMismatchException` bug I described in my answer. Is that what you did, or was it something different? – Kevin J. Chase Oct 29 '16 at 17:19
  • Any word on this? Is there a way to reliably trigger that endless loop in Menu 2? Any comment on my answer below? – Kevin J. Chase Nov 03 '16 at 19:53

1 Answers1

2

From the intro section of java.util.Scanner docs (emphasis mine):

When a scanner throws an InputMismatchException, the scanner will not pass the token that caused the exception, so that it may be retrieved or skipped via some other method.

Without the details, your while(true) loop is:

while (true) {
    try{
        choice = userInput.nextInt();
        switch (choice){
            case 1:
             ...
        }
    } catch (InputMismatchException exc){
        // Do nothing.
    }
}

When the user enters something that can't be converted to an integer, the Scanner throws an InputMismatchException, which you catch and ignore. Then the while loop goes back to the top, where it tries to execute userInput.nextInt()... but the Scanner is still looking at the same invalid input, so it immediately throws another InputMismatchException, which you catch and ignore again. Execution continues at the top of the while loop, where it calls nextInt() again... and the cycle continues forever.

You have to force the Scanner to skip the bad input, so your catch block should look something like this:

}catch (InputMismatchException exc){
    System.out.println("\t\t[chastise the user in Greek]\n");  
    userInput.next();  // Skip invalid input.
}

Other Advice

As a general rule, lots of small methods are easier to understand than one large method. The nested while loops and switch statement were especially hard to follow. I was only able to find the bug by breaking that gigantic main method into many smaller, private static methods.

At the very least, each menu item could be handled in its own method. I also got rid of the break label by putting the whole menu into a separate method, which returned a boolean indicating whether the user was done or not. That reduced the whole loop inside main to:

boolean done = false;
while (! done) {
    try{
        done = handleUserInput(queue, userInput);
    } catch (InputMismatchException exc) {
        System.out.println("\nINPUT ERROR\n");
        userInput.next();
    } // Other catch blocks as before...
}

My handleUserInput doesn't do much --- it gets user input, determines which method should handle that input, and then returns true or false... It could be made simpler than this, too.

private static boolean handleUserInput(
  final ArrayQueue queue,
  final Scanner userInput
) {
    boolean done = false;
    printMenu();
    int choice = userInput.nextInt();
    switch (choice) {
        case 1:
            addToQueue(queue, userInput);
            break;
        case 2:
            removeFromQueue(queue, userInput);
            break;
        case 3:
            displayQueue(queue);
            break;
        case 4:
            printExitMessage();
            done = true;
            break;
        default:
            break;
    }
    return done;
}

Splitting the various menu activities into separate methods made them much easier to follow. For example, when the logic was all mixed together in main, it was hard to tell if variables like carNum or answer were part of the problem. In this version, carNum is a local variable trapped inside the addToQueue method, so when I'm working anywhere else, I can completely ignore it.

Kevin J. Chase
  • 3,856
  • 4
  • 21
  • 43