CS1092: Tutorial Sheet: No 3 Exceptions and Files Tutor s Guide Preliminary This tutorial sheet requires that you ve read Chapter 15 on Exceptions (CS1081 lectured material), and followed the recent CS1092 lectures on exceptions and text-based input and output (which are covered in Chapter 17 and 18 of John Latham s book). Not that you need to be reminded by now, but don t forget to bring your log book, in which you should have documented discussion notes or answers for the following questions, to the tutorial session. Questions 1. Discuss with your tutor and fellow tutees the role, and pros & cons, of exceptions in Java. The following set of questions may be used to motivate and structure the discussion. What are exceptions? When do they occur? What can the programmer do with them? Given some examples of different exceptions and explain how they might arise? What s special about the subclass RuntimeException? Does this special property hold for any other throwable class? What happens when an exception is thrown? Can they be caught, if so how? Are all exceptions pre-defined as part of the Java API, or can users define their own. If so, how, and when this might this happen? What alternative might there be to throwing exceptions?...??? The above questions should give sufficient direction for your group to have reasonable discussion based on the last couple of lectures in CS1081 and one lecture in CS1092. However, do note that they haven t had any exercise of this material in the laboratory (so far) and have only recently seen exceptions appearing in the examples we use in lectures. For their fourth laboratory session this semester, they will be getting some exercise in writing Java programs that do actually catch and throw exceptions! They had no (formal) exercise on exceptions in the CS1081 laboratory. 2. The following program was used in the lectures. import java.io.bufferedreader; import java.io.filewriter; import java.io.filereader; import java.io.inputstreamreader; import java.io.ioexception; import java.io.printwriter;
public class TextTranslatorProgram private final TextTranslator texttranslator; public TextTranslatorProgram (StringTranslator requiredstringtranslator) texttranslator = new TextTranslator(requiredStringTranslator); // TextTranslatorProgram public void translate(string [] args) throws IOException, ArgumentException, TextTranslatorException BufferedReader input; PrintWriter output; if (args.length < 1 args[0].equals("-")) input = new BufferedReader(new InputStreamReader(System.in)); input = new BufferedReader(new FileReader(args[0])); if (args.length < 2 args[1].equals("-")) output = new PrintWriter(System.out, true); output = new PrintWriter(new FileWriter(args[1])); if (args.length > 2) throw new ArgumentException("Too many arguments"); texttranslator.translate(input, output); input.close(); output.close(); // translate // class TextTranslatorProgram (a) explain to your tutor what this class does and how it can be used? The students, of course, have an advantage over you here - they ve seen the full set of class definitions - (so, just for completeness, I ve attached them as an appendix). Quite simply, a TextTranslatorProgram sets up a reader and writer on either files or standard in or out (depending on the command line arguments) and applies the translate method of the constructed TextTranslator to it. The latter will apply its StringTranslator on a line by line basis to those from the reader, the output being passed to the writer. (b) what s the purpose of the BufferedReader wrapper? As you probably appreciate the Java API is particularly messy in the area of input and output, so, in order to try to keep the story as simple and as clean as possible, we are using the reader and writer classes as much as possible for textual input and output. (Binary I/O is introduced a little later.) The BufferedReader wrapper is present, principally in our case, to provide a readline method. 2
(c) why is InputStreamReader used to wrap around System.in? The InputStreamReader wrapper acts as a bridge between byte streams and character streams. System.in is an InputStream. Our approach is to convert to a character stream for reading text. (d) what happens if a name is passed as args[0] for which a file doesn t exist? A FileNotFoundException is raised by the FileReader constructor. Assuming the given file name is filename the message of the exception is filename (The system cannot find the file specified). (e) what happens if a name passed as args[0] refers to a directory (or folder) in the current directory (or folder). Again, a FileNotFoundException is raised with message directoryname (Access is denied), assuming directoryname as the given file name. (f) what happens if a name passed as args[1] refers to a file which is not writable? Yet again, a FileNotFoundException is raised with message name (Access is denied), assuming name as the given name. (g) what happens if a name passed as args[1] refers to a file which doesn t exist? A file is created with the given name and, assuming no other problems, the text translator will write to it. (h) modify the translate method to ensure it doesn t overwrite an existing writable file (name passed in args[1]). As it stands, an exception will be thrown if a readable but non-writable file/directory name is passed as args[1] when attempting to create the FileWriter. So to ensure the program doesn t overwrite a writable file one must check either whether it exists or whether it is writable. To do this, create a File object, say temp with the given name from args[1], then use the methods temp.exists() or temp.canwrite() to determine whether an exception should be thrown. if (args.length < 2 args[1].equals("-")) output = new PrintWriter(System.out, true); File temp = new File(args[1]); if (temp.canwrite()) throw new ArgumentException(args[1] + " exists and is writable"); output = new PrintWriter(new FileWriter(args[1])); Note that if a (writable) directory name is passed as args[1] then an ArgumentException is also thrown. As below, one might wish to generate a more meaningful message for such a situation. (i) modify the translate method to produce a more meaningful message when a directory name is passed as args[0]. Here, use the isdirectory() method of the File class to determine whether a directory has been passed as argument. If so, then thrown an ArgumentException with an appropriate message. if (args.length < 1 args[0].equals("-")) input = new BufferedReader(new InputStreamReader(System.in)); File temp = new File(args[0]); if (temp.isdirectory()) throw new ArgumentException(args[0] + " is a directory"); input = new BufferedReader(new FileReader(args[0])); 3
Appendix public abstract class StringTranslator public abstract String translate(string string); // class StringTranslator public class TextTranslatorException extends Exception public TextTranslatorException(String message) super(message); // TextTranslatorException // class TextTranslatorException import java.io.bufferedreader; import java.io.ioexception; import java.io.printwriter; public class TextTranslator private final StringTranslator stringtranslator; public TextTranslator(StringTranslator requiredstringtranslator) stringtranslator = requiredstringtranslator; // TextTranslator public void translate(bufferedreader input, PrintWriter output) throws IOException, TextTranslatorException if (input == null) throw new TextTranslatorException("Input is null"); if (output == null) throw new TextTranslatorException("Output is null"); String line; while ((line = input.readline())!= null) String translatedline = stringtranslator.translate(line); if (translatedline!= null) output.println(translatedline); // while // translate // class TextTranslator import java.io.bufferedreader; import java.io.filewriter; 4
import java.io.filereader; import java.io.inputstreamreader; import java.io.ioexception; import java.io.printwriter; public class TextTranslatorProgram private final TextTranslator texttranslator; public TextTranslatorProgram (StringTranslator requiredstringtranslator) texttranslator = new TextTranslator(requiredStringTranslator); // TextTranslatorProgram public void translate(string [] args) throws IOException, ArgumentException, TextTranslatorException BufferedReader input; PrintWriter output; if (args.length < 1 args[0].equals("-")) input = new BufferedReader(new InputStreamReader(System.in)); input = new BufferedReader(new FileReader(args[0])); if (args.length < 2 args[1].equals("-")) output = new PrintWriter(System.out, true); output = new PrintWriter(new FileWriter(args[1])); if (args.length > 2) throw new ArgumentException("Too many arguments"); texttranslator.translate(input, output); input.close(); output.close(); // translate // class TextTranslatorProgram public class CountingStringTranslator extends StringTranslator private int linecount = 0; private int linenumberdigits; private String leadingzeroes; public CountingStringTranslator(int requiredlinenumberdigits) 5
linenumberdigits = requiredlinenumberdigits; leadingzeroes = ""; for (int count =1; count <= linenumberdigits; count++) leadingzeroes += "0"; // class CountingStringTranslator public String translate(string line) linecount++; String linenumber = leadingzeroes + linecount; linenumber = linenumber.substring(linenumber.length() - linenumberdigits); return linenumber + " " + line; // translate // class CountingStringTranslator public class LineCount public static void main(string [] args) try new TextTranslatorProgram(new CountingStringTranslator(3)).translate(args); // try catch (Exception exception) System.err.println(exception); // catch // main // class LineCount public class StringMatcher extends StringTranslator private String matchstring; public StringMatcher( String reqdstring ) matchstring = reqdstring; public String translate(string string) return (string.indexof(matchstring)==-1?null:string); public class LineFilter public static void main(string [] args) 6
try if ( args.length < 1) throw new ArgumentException("Insufficient arguments for LineFilter"); String [] newargs = new String [args.length-1]; for (int i = 1; i < args.length; i++) newargs[i-1] = args[i]; new TextTranslatorProgram( new StringMatcher(args[0]) ).translate(newargs); // try catch (Exception exception) System.err.println(exception); // catch // main // class LineFilter public class CompositeStringTranslator extends StringTranslator StringTranslator first; StringTranslator second; public CompositeStringTranslator(StringTranslator reqdfirst, StringTranslator reqdsecond) first = reqdfirst; second = reqdsecond; public String translate (String inputstring) if (inputstring!= null) String temp = first.translate(inputstring); return (temp!=null?second.translate(temp):null); return null; // class CompositeStringTranslator public class LineCountFilter public static void main(string [] args) try if ( args.length < 1) throw new ArgumentException("Insufficient arguments for LineCountFilter"); String [] newargs = new String [args.length-1]; for (int i = 1; i < args.length; i++) newargs[i-1] = args[i]; new TextTranslatorProgram( new CompositeStringTranslator( 7
new CountingStringTranslator(3), new StringMatcher(args[0]))).translate(newArgs); // try catch (Exception exception) System.err.println(exception); // catch // main // class LineCountFilter 8