Wednesday, November 30, 2016

Error Handling Challenge

In this exercise, you will learn how to handle exceptions and how to validate inputs.

The base code is:
/**
 * Class MyCopy does just that, it copies a file into another.
 * @author Zoltan Szabo
 * @version 1.0.0
 */
public class MyCopy{
    /**
     * Entry point to application.
     * If output file exists or input file does not exists, it sets errorlevel.
     * @param args[0] source file name
     * @param args[1] destination file name
     */
    public static void main(String args[]){
            File inFile=new File(args[0]);  //source file name
            File outFile=new File(args[1]); //destination file name

            Scanner input = new Scanner(inFile);
            PrintWriter output = new PrintWriter(outFile);
            while(input.hasNext()){                    //read input file until it reaches the end of file
                    output.println(input.nextLine());
                }
            output.close();
            input.close();
    }
}

Now let's try to compile it with BlueJ ( any other IDE will do that does not give you too much help. Try to find the problems logically using the Java API )

1. We get an error message - "cannot find symbol - class File"  This suggest the missing class, so we should think about importing the class.
Solution:
Go to Java API and search for File class
Copy the path
Add the import java.io.File; to your code
2. We get an error message - "cannot find symbol - class Scanner"  This suggest the missing class, so we should think about importing the class.
Solution:
Go to Java API and search for Scanner class
Copy the path
Add the import java.util.Scanner; to your code
3. We get an error message - "cannot find symbol - class PrintWriter"  This suggest the missing class, so we should think about importing the class.
Solution:
Go to Java API and search for PrintWriter class
Copy the path
Add the import java.io.PrintWriter; to your code
4. At this point, we seem like have all the classes imported, but we get another type of error:
"unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown"
Let's look at the Scanner class constructor to see why we get this error message.
We see that
public Scanner(File source)
        throws FileNotFoundException
and FileNotFoundException is thrown - if source is not found

So, you can add public static void main(String args[])throws java.io.FileNotFoundException to your main method header or just public static void main(String args[])throws FileNotFoundException and then also import java.io.FileNotFoundException; to have the class available.
You should also add the docstring tag @throws since you need to explain why you are throwing the exception.

@throws java.io.FileNotFoundException if source is not found

5. Now, if we run the application without entering a filename argument, we'll get an error:
"java.lang.ArrayIndexOutOfBoundsException:0" This error was not handled yet, so we need to see how we can handle it.  This time, we'll actually will catch the exception and handle it locally instead of throwing it to the JVM to handle.
So, add the try block embracing all statements in the main method and the catch block to the code:

catch(ArrayIndexOutOfBoundsException e){
  System.out.println("You need to type the input and output files for this utility to work properly. \"MyCopy [[drive:\\]path]input.txt [[drive:\\]path]output.txt\"");
   System.exit(2017);
        }

ArrayIndexOutOfBoundsException class is in java.lang so you will not have to import it, but as a learning process, you should add the import anyway and you should also lookup the class in Java API documentation.

6. Now, you should also look at the other classes in your program to see if more exception might be thrown by any one of those classes.
public PrintWriter(File file)
            throws FileNotFoundException
You can see that PrintWriter class also throws an exception, so you can also handle that locally by surrounding the PrintWriter related statements in a try block and catching the exception.

 catch(FileNotFoundException e){
    System.out.println("The destination file " + outFile + " might not have the appropriate rights.");
    System.exit(2016);
    }

If you look further into the methods like nextLine method in the Scanner class also throws exceptions, but instead of handling every exception individually,

public String nextLine()
    NoSuchElementException - if no line was found
    IllegalStateException - if this scanner is closed


you can see in the Java API the hierarchy that it might make sense to throw the Exception object at a method level to handle all other than FileNotFoundException will be thrown and handled by the common class and due to polymorphism, the exception will be handled.

7. You can still have another exception thrown if you do not give the proper input arguments to the application at run time due to the args[0] and args[1] usage.  It can throw and ArrayIndexOutOfBoundsException and that exception should not let the program continue to run.  Thus, add another try/catch block to handle that case as well.

        catch(ArrayIndexOutOfBoundsException e){
            System.out.println("Type the input and output files. \"MyCopy [[drive:\\]path]input.txt [[drive:\\]path]output.txt\"");
            System.exit(2017);
        }

8. Now, you just have to add the input and output validation where we can terminate the application and return an exit code to the OS to show status of program exit.

So, the final program should look like this:

import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
//you don't really need it since it is in java.lang, but nice to have it to remind you 
import java.lang.ArrayIndexOutOfBoundsException;
/**
 * Class MyCopy does just that, it copies a file into another.
 * @author Zoltan Szabo
 * @version 1.0.0
 */
public class MyCopy
{
    /**
     * Entry point to application.
     * If output file exists or input file does not exists, it sets errorlevel.
     * @param args[0] source file name
     * @param args[1] destination file name

     * @throws Exception to handle all exceptions not specifically handled locally
     */
    public static void main(String args[])throws Exception {
        try{
            File inFile=new File(args[0]);  //source file name
            File outFile=new File(args[1]); //destination file name

            if (!inFile.exists()){           //Checks if the file name exists
                System.out.println("Source file " + inFile + " does not exists.");
                System.exit(2014);            // Exit the program.
            }

            Scanner input = new Scanner(inFile);

            if (outFile.exists()){           //Checks if the file name already exists
                System.out.println("The destination file " + outFile + " already exists.");
                System.exit(2015);            // Exit the program.
            }

            try{
                PrintWriter output = new PrintWriter(outFile);
                while(input.hasNext()){                    //read input file until it reaches the end of file
                    //System.out.println(input.nextLine());
                    output.println(input.nextLine());
                }
                output.close();
            }
            catch(FileNotFoundException e){
                System.out.println("The destination file " + outFile + " might not have the appropriate rights.");
                System.exit(2016);
            }

            input.close();
        }
        catch(ArrayIndexOutOfBoundsException e){
            System.out.println("Type the input and output files. \"MyCopy [[drive:\\]path]input.txt [[drive:\\]path]output.txt\"");
            System.exit(2017);
        }

    }
}

No comments:

Post a Comment