package lectures.exceptions;
import util.annotations.WebDocuments;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * COOPERATIVE EXECEPTION PROCESSING
 * We finally see cooperative exception processing, exceptions thrown in called
 * methods being handled by calling methods.
 * 
 * Look first at echoLines and numberOfInputLines, then at main.
 *  
 * 
 */
@WebDocuments({"Lectures/Exceptions.pptx", "Lectures/Exceptions.pdf", "Videos/Exceptions.avi"})
public class LinesReaderAndPrinterPropagatingExceptions {
    
    static BufferedReader input = new BufferedReader(new InputStreamReader(
            System.in));
    /**
     * This method no longer has code to "catch" the IOException that can
     * result from readLine as it does not have enough context to handle the exception. 
     * To document this, it has a header with the throws clause, to warn its caller about this.
     * 
     * (The throws clause is usually on the same line as the rest of the header, it is
     * just on a different line here to make it easier to comment out later)
     */
    static void echoLines(int numberOfInputLines) 
     /*
      * Comment out the throws class when instructed
      */
            throws IOException 
    {
        for (int inputNum = 0; inputNum < numberOfInputLines; inputNum++) {
            System.out.println(input.readLine());
        }
    }
    /**
     * This method too no longer has code to "catch" the exception and documents this fact 
     * for its caller.
     */
    static int numberOfInputLines(String[] args)
     /*
      * Comment out the throws class when instructed
      */
            throws ArrayIndexOutOfBoundsException 
        {
        /*
         * Put breakpoint on the next line.
         * 
         */
        return Integer.parseInt(args[0]); 
    }
    
    /**  
     * This main now handles exceptions not caught in the two methods it calls.      
     */
    public static void main(String args[]) 
     /*
      * Uncomment the throws clause when instructed.
      */
//      throws IOException, ArrayIndexOutOfBoundsException 
    {       
                
        try { 
            echoLines(numberOfInputLines(args)); // if this echoLines fails where will the program go next?
        } 
        /*
         * Comment out this catch when instructed
         */
        catch (ArrayIndexOutOfBoundsException e) { 
            System.out
                    .println("Did not enter an argument. Assuming a single input line.");
            // Why is this try in a catch?
            try {
                echoLines(1);  // if this echoLines fails where will the program go next?
            } catch (IOException ioe) {
                System.out
                        .println("Did not input the one input string, which is the default"
                                + " in case of missing argument, before input was closed. ");
            }
        } 
        /*
         * Comment out this catch when instructed
         */
        catch (IOException e) 
        {
            System.out
                    .println("Did not input the correct number of input strings before input was closed. ");
        }
    }
}
/*
 * 
 * Put breakpoint in numberOfInputLine and debug-run the code without 
 * entering a main argument. Press F6 (step over) to follow the control flow
 * when the statement is executed.
 * 
 * (T/F) LinesReaderAndPrinterPropagatingExceptions with no argument executes 
 * (one or more statements in) a try block of numberOfInputLines. 
 * 
 * (T/F) LinesReaderAndPrinterPropagatingExceptions with no argument executes 
 * (one or more statements in) a catch block of numberOfInputLines. 
 * 
 * (T/F) LinesReaderAndPrinterPropagatingExceptions with no argument executes
 *  (one or more statements in) a try block of main.
 * 
 * (T/F) LinesReaderAndPrinterPropagatingExceptions with no argument executes
 *  (one or more statements in) a catch block of main.
 * 
 * (T/F) If method p calls method q, and the execution of q results in an exception 
 * of type E not handled by a catch block of q, then control transfers to a 
 * catch block of p that can handle an exception of type E.
 * 
 * (T/F) LinesReaderAndPrinterPropagatingExceptions with no argument executes
 * (one or more statements in) a try block of echoLines.
 * 
 * (T/F) LinesReaderAndPrinterPropagatingExceptions with no argument reads input
 * from user.
 *  
 * (T/F) A catch block can recover from an exception thrown by executing some 
 * error-prone method by nesting it in a try block.
 *  
 * Comment out throws clause in the header of echoLines.
 * 
 * What happens?  
 * 
 * (T/F) If a method p executes a statement that can throw an IOException, 
 * then p must either catch the exception or acknowledge in the header 
 * that it is not catching the exception.  
 * 
 * Uncomment the throws clause in the header of echoLines.
 * 
 * Comment out the throws clause in the header of numberOfInputLines.
 * 
 * What happens?  
 * 
 * (T/F) If a method p executes a statement that can throw an ArrayIndexOutOfBoundsException, 
 * then p must either catch the exception or acknowledge in the header 
 * that it is not catching the exception.  
 * 
 * Uncomment the throws clause in the header of numberOfInputLines.
 * 
 * Java has two kinds of exceptions, checked and unchecked.
 * 
 * If a method p executes a statement that can throw a checked exception 
 * then p must either catch the exception or acknowledge in the header 
 * that it is not catching the exception.  
 * 
 * Unchecked exceptions do not have to be acknowledged in headers or caught in
 * catch blocks.
 * 
 * (T/F) IOException is a checked exception.
 * 
 * (T/F) ArrayIndexOutOfBoundsException is a checked exception. 
 *  
 * Comment out the IOException catch block in main. 
 * What happens?
 * 
 * Uncomment the throws clause in the header of main.
 * What happens?
 * 
 * (T/F) If a method p can execute a statement that directly or indirectly can throw
 * an IOException that is catchable in p, then p must either catch the exception or
 * acknowledge in the header that it is not catching the exception and thus propagating
 * or throwing the exception to its caller. 
 * 
 *  
 *  Uncomment the IOException catch block in main.
 *  
 *  So now both the throws clause and the corresponding catch block are in the
 *  program.
 *  
 *  (T/F) It is possible for an IOException to be both caught by main and 
 *  propagated to its caller.
 *  
 *  Hint: You can answer this question based on what Java allows, or you could try adding
 *  "throw e;" at the end of the IOException catch block.
 *  
 *  (T/F) Java allows checked exceptions to be acknowledged in method headers even
 *  if they are not propagated to callers.
 *  
 *  The Halting problem says that it is not possible for Java to always know if some
 *  statement will actually be executed at runtime.
 *  
 *  (T/F) The halting problem prevents Java from always knowing if an exception will actually 
 *  be thrown by a try block.
 *  
 *  As we have seen, at compile time we do not know which implementation of  a
 *  method will actually be invoked to service a method call. If the call is an external call
 *  targeted at a variable typed using an interface, then answer depends on the class of 
 *  the object assigned to the variable. If the call is made in class C to an 
 *  internal (inherited or declared) method m of C, then the method actually invoked 
 *  may be an overriding method m in some subclass of C. In both cases, the method
 *  call is dynamically dispatched at runtime. This means the header of a method
 *  in an interface or class must advertise the union of all exceptions
 *  thrown by implementing and overriding methods respectively.
 *   
 *  (T/F) Dynamic dispatching prevents Java from always knowing which exceptions will be
 *  thrown by a method call.
 *   
 *  (T/F) When in doubt, is it better to be conservative and over advertise the thrown
 * checked exceptions in a method header so that the a caller can always handle an exception 
 * that propagates to it.
 * 
 *

 * Two questions that arise are:
 * 
 * How do programmers tell Java which exceptions are checked and which are not?
 * Why two kinds of exceptions?
 * 
 * 
 * An unchecked exception has {@link RuntimeException} as a superclass, a checked
 * exception does not. Hover over the name to see the full description.
 * 
 * Now the more interesting question: Why two kinds of exceptions?

 * Would you revolt if all exceptions were checked, regardless of context? 
 * If so, why?
 * 
 * Hint: There certain kinds of exceptions that can never be avoided no matter
 * how well you program because of external forces such as users and other remote programs. 
 * Certain kinds of exceptions can be avoided, at least in some cases.
 * 
 * In this example, the ArrayIndexOutOfBoundsException is a result of user
 * error but it can be avoided in other contexts.
 *  
 * Problem 1: Given the above, how does the programmer of numberOfInputLines (who
 * does not want to handle the exception because of lack of context) inform its callers about
 * this uncaught exception so one or more of them can handle it?
 * 
 * Problem 2: How does the programmer also force some caller in the stack of
 * calls to handle the lack of a user argument (since the current exception is
 * unchecked)?
 * 
 * Problem 3: The exception ArrayIndexOutBoundsException can be as unindicative of the cause
 * of the exception - missing argument - to the caller as the error message about 
 * array index out of bounds was to the end user.
 * 
 * Go to LinesReaderAndPrinterUsingProgrammerDefinedException
 * 
 * 
 *  
 */