package plume; import java.io.*; // TODO: A better name would be LineNumberException. // And then it needn't really extend IOException. // TODO: Maybe the constructors should take a Reader and check at run time // whether it's a LineNumberReader. Easier for clients, but easier to // forget to provide the right type of Reader, too. /** * This class extends IOException by also reporting a file name and line * number at which the exception occurred. It requires use of a * {@link LineNumberReader}. **/ public class FileIOException extends IOException { static final long serialVersionUID = 20050923L; public final /*@Nullable*/ String fileName; public final int lineNumber; /// /// Empty constructor /// public FileIOException() { super(); fileName = null; lineNumber = -1; } /////////////////////////////////////////////////////////////////////////// /// Without a message (with a Throwable instead) /// // If cause is null, the super call throws a null pointer exception. // This looks like a JDK bug. -MDE 12/9/2008 public FileIOException(/*@Nullable*/ Throwable cause) { // The "super(Throwable)" constructor exists in Java 6 and later. // For backward compatibility, use the initCause method instead. initCause(cause); fileName = null; lineNumber = -1; } /////////////////////////////////////////////////////////////////////////// /// Without a Reader /// public FileIOException(/*@Nullable*/ String s) { super(s); fileName = null; lineNumber = -1; } public FileIOException(/*@Nullable*/ String s, /*@Nullable*/ Throwable cause) { // The "super(String, Throwable) constructor exists in Java 6 and later. // For backward compatibility, use the initCause method instead. super(s); initCause(cause); fileName = null; lineNumber = -1; } // Design choice: require filename and linenumber, don't support // interface with just one or the other. public FileIOException(/*@Nullable*/ String s, /*@Nullable*/ String fileName, int lineNumber) { super(s); this.fileName = fileName; this.lineNumber = lineNumber; } public FileIOException(/*@Nullable*/ String s, /*@Nullable*/ Throwable cause, /*@Nullable*/ String fileName, int lineNumber) { // The "super(String, Throwable) constructor exists in Java 6 and later. // For backward compatibility, use the initCause method instead. super(s); initCause(cause); this.fileName = fileName; this.lineNumber = lineNumber; } /////////////////////////////////////////////////////////////////////////// /// Without a filename or File /// // I cannot infer the filename from the reader, because LineNumberReader // gives no access to the underlying stream. public FileIOException(/*@Nullable*/ LineNumberReader reader, /*@Nullable*/ Throwable cause) { this(reader, /*fileName=*/ (/*@Nullable*/ String)null, cause); } public FileIOException(/*@Nullable*/ String s, /*@Nullable*/ LineNumberReader reader) { this(s, reader, /*fileName=*/ (/*@Nullable*/ String)null); } public FileIOException(/*@Nullable*/ String s, /*@Nullable*/ LineNumberReader reader, /*@Nullable*/ Throwable cause) { this(s, reader, /*fileName=*/ (/*@Nullable*/ String)null, cause); } /////////////////////////////////////////////////////////////////////////// /// With a filename /// public FileIOException(/*@Nullable*/ String s, /*@Nullable*/ LineNumberReader reader, /*@Nullable*/ String fileName) { super(s); this.fileName = fileName; this.lineNumber = getLineNumber(reader); } public FileIOException(/*@Nullable*/ LineNumberReader reader, /*@Nullable*/ String fileName, /*@Nullable*/ Throwable cause) { // The "super(Throwable) constructor exists in Java 6 and later. // For backward compatibility, use the initCause method instead. initCause(cause); this.fileName = fileName; this.lineNumber = getLineNumber(reader); } public FileIOException(/*@Nullable*/ String s, /*@Nullable*/ LineNumberReader reader, /*@Nullable*/ String fileName, /*@Nullable*/ Throwable cause) { // The "super(String, Throwable) constructor exists in Java 6 and later. // For backward compatibility, use the initCause method instead. super(s); initCause(cause); this.fileName = fileName; this.lineNumber = getLineNumber(reader); } /////////////////////////////////////////////////////////////////////////// /// With a File /// public FileIOException(/*@Nullable*/ String s, /*@Nullable*/ LineNumberReader reader, File file) { this(s, reader, file.getName()); } public FileIOException(/*@Nullable*/ String s, /*@Nullable*/ LineNumberReader reader, File file, /*@Nullable*/ Throwable cause) { this(s, reader, file.getName(), cause); } public FileIOException(/*@Nullable*/ LineNumberReader reader, File file, /*@Nullable*/ Throwable cause) { // The "super(Throwable) constructor exists in Java 6 and later. // For backward compatibility, use the initCause method instead. initCause(cause); this.fileName = file.getName(); this.lineNumber = getLineNumber(reader); } /////////////////////////////////////////////////////////////////////////// /// Utility and helper methods /// public String toString() { String result = super.toString(); if (fileName != null) { result += " in file " + fileName; } if (lineNumber != -1) { result += " at line " + lineNumber; } return result; } // Infers the line number from the "reader" field. // Assumes the "reader" field is already set. // Not a setter method because field lineNumber is final, but // still clearer to abstract out. private int getLineNumber(/*@Nullable*/ LineNumberReader reader) { if (reader != null) { return reader.getLineNumber(); } else { return -1; } } }