001package Torello.Java;
002
003import java.io.*;
004
005/**
006 * Used by methods that accept <CODE>'target directory'</CODE> parameters to validate that the
007 * parameter's contents actually point to a <B>both</B> valid <B>and</B> writable location on disk.
008 * 
009 * <BR /><EMBED CLASS='external-html' DATA-FILE-ID=WRITABLE_DIR_EX>
010 */
011public class WritableDirectoryException extends RuntimeException
012{
013    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUIDEX>  */
014    public static final long serialVersionUID = 1;
015
016    /**
017     * This boolean allows a user to decide whether the zero-length {@code String} should force an
018     * exception throw when passed to this class' {@link #check(String)} method.  When this boolean
019     * is set to {@code TRUE}, the class will check whether or not the directory named by the
020     * {@code String} returned by a call to {@code System.getProperty("user.dir")} is writable.
021     *   
022     * <BR /><BR />An invocation to method {@code System.getProperty("user.dir")} returns a
023     * {@code String} that contains the current user's home directory.  If this directory is
024     * writable, then no exception throw shall occur when testing and passing a zero-length 
025     * {@code String} to the {@code 'directory'} parameter of the {@code check(String)} method.
026     * 
027     * <BR /><BR />When this parameter is set to {@code FALSE}, and when a zero-length 
028     * {@code String} is passed to the {@code 'directory'} parameter of the {@code 'check(String)'}
029     * method.  
030     * 
031     * <BR /><BR /><B>DEFAULT:</B> The default behavior of method {@link #check(String)} is to
032     * throw an exception whenever the zero-length {@code String} is passed.
033     */
034    public static boolean THROW_ON_ZERO_LENGTH_STRING = true;
035
036    /** Constructs a {@code WritableDirectoryException} with no detail message. */
037    public WritableDirectoryException()
038    { super(); }
039
040    /**
041     * Constructs a {@code WritableDirectoryException} with the specified detail message.
042     * @param message the detail message.
043     */
044    public WritableDirectoryException(String message)
045    { super(message); }
046
047    /**
048     * Constructs a new exception with the specified detail message and cause.
049     * 
050     * <BR /><BR /><B CLASS=JDDescLabel>NOTE:</B>
051     * 
052     * <BR /><BR />The detail message associated with cause is not automatically incorporated into
053     * this exception's detail message.
054     * 
055     * @param message The detail message (which is saved for later retrieval by the
056     * {@code Throwable.getMessage()} method).
057     * 
058     * @param cause the cause (which is saved for later retrieval by the
059     * {@code Throwable.getCause()} method).  (A null value is permitted, and indicates that the
060     * cause is nonexistent or unknown.)
061     */
062    public WritableDirectoryException(String message, Throwable cause)
063    { super(message, cause); }
064
065    /**
066     * Constructs a new exception with the specified cause and a detail message of
067     * {@code (cause==null ? null : cause.toString())} (which typically contains the class and
068     * detail message of cause).  This constructor is useful for exceptions that are little more
069     * than wrappers for other {@code Throwable's}.
070     * 
071     * @param cause The cause (which is saved for later retrieval by the
072     * {@code Throwable.getCause()} method).  (A null value is permitted, and indicates that the
073     * cause is nonexistent or unknown.)
074     */
075    public WritableDirectoryException(Throwable cause)
076    { super(cause); }
077
078   /**
079     * Checks that a directory-name is valid and ready for saving image files.
080     * 
081     * @param directory  This is the directory-name to be tested.
082     * 
083     * @throws WritableDirectoryException Throws under any of the following circustances:
084     * 
085     * <BR /><BR /><UL CLASS=JDUL>
086     * 
087     * <LI> Parameter {@code 'directory'} has been passed null.
088     *      <BR /><BR />
089     *      </LI>
090     * 
091     * <LI> Parameter {@code 'directory'} is a zero length {@code String}, and the configurable
092     *      {@code public} and {@code static} boolean {@link #THROW_ON_ZERO_LENGTH_STRING} has
093     *      been set to {@code TRUE}.
094     *      <BR /><BR />
095     *      </LI>
096     * 
097     * <LI> The directory specified by parameter {@code 'directory'} does not exist anywhere on the
098     *      file-system.
099     *      <BR /><BR />
100     *      </LI>
101     *
102     * <LI> The file specified by parameter {@code 'directory'} exists, but is a file, rather than
103     *      a directory.
104     *      <BR /><BR />
105     *      </LI>
106     *
107     * <LI> This method was unable to write a small, temporary file to that directory, and in 
108     *      the process of attempting to do so caused a Java {@code IOException} to be thrown.
109     * 
110     *      <BR /><BR />Note that in this case, the {@code IOException} that was thrown (and
111     *      caught) will be assigned as the {@code 'cause'} exception, and may be retrieved by
112     *      calling {@code 'this'} exception's {@code getCause()} method.
113     *      </LI>
114     *
115     * </UL>
116     */
117    public static void check(String directory)
118    {
119        if (directory == null) throw new WritableDirectoryException
120            ("Parameter 'directory' was passed null");
121
122        if (directory == "")
123        {
124            if (THROW_ON_ZERO_LENGTH_STRING) throw new WritableDirectoryException(
125                "You have passed the zero-length String to the class " +
126                "WritableDirectoryException's 'check' method.  If this is the desired behavior, " +
127                "there is a configurable, static, boolean named 'THROW_ON_ZERO_LENGTH_STRING' " + 
128                "in this class that should be set to FALSE.  Currently, when the zero-length " +
129                "String is passed here, an exception (this one) is thrown immediately.  If this " +
130                "configuration-boolean were TRUE, the directory checked would be the one " +
131                "returned by a call to System.getProperty(\"user.dir\")"
132            );
133
134            directory = System.getProperty("user.dir");
135        }
136
137        check(new File(directory));
138    }
139
140   /**
141     * Checks that a directory-name is valid and ready for saving image files.
142     * 
143     * @param directory  This is the directory-name to be tested.
144     * 
145     * @throws WritableDirectoryException Throws under any of the following circustances:
146     * 
147     * <BR /><BR /><UL CLASS=JDUL>
148     * 
149     * <LI> Parameter {@code 'directory'} has been passed null.
150     *      <BR /><BR />
151     *      </LI>
152     * 
153     * <LI> The directory specified by parameter {@code 'directory'} does not exist anywhere on the
154     *      file-system.
155     *      <BR /><BR />
156     *      </LI>
157     *
158     * <LI> The file specified by parameter {@code 'directory'} exists, but is a file, rather than
159     *      a directory.
160     *      <BR /><BR />
161     *      </LI>
162     *
163     * <LI> This method was unable to write a small, temporary file to that directory, and in 
164     *      the process of attempting to do so caused a Java {@code IOException} to be thrown.
165     * 
166     *      <BR /><BR />Note that in this case, the {@code IOException} that was thrown (and
167     *      caught) will be assigned as the {@code 'cause'} exception, and may be retrieved by
168     *      calling {@code 'this'} exception's {@code getCause()} method.
169     *      </LI>
170     *
171     * </UL>
172     */
173    public static void check(File directory)
174    {
175        if (directory == null) throw new WritableDirectoryException
176            ("Parameter 'directory' was passed null");
177
178        // Make sure that the directory name exists on the file-system.  If not throw exception
179        if (! directory.exists()) throw new WritableDirectoryException(
180            "The directory-name [" + directory.toString() + "], that you have passed does not " +
181            "exist, or could not be found be the file-system."
182        );
183
184        // Make sure that the directory-named is actually a directory, not a file, link, etc...
185        if (! directory.isDirectory()) throw new WritableDirectoryException(
186            "The directory-name [" + directory.toString() + "], that you have passed was found, " +
187            "but it is not a valid directory-name on the file-system."
188        );
189
190        // Make sure that the directory provided is writable, or throw an exception.
191        try
192        {
193            File f = File.createTempFile("test", "tmp", directory);
194            f.delete();
195        }
196
197        catch (IOException ioe)
198        {
199            throw new WritableDirectoryException(
200                "There was a JavaIOException when attempting to write a file to the directory-name " +
201                '[' + directory.toString() + "].  Please see this exception's getCause() " +
202                "throwable for more details.",
203                ioe
204            );
205        }
206    }
207}