1
\$\begingroup\$

I'm writing a program to read two files supplied as arguments in the command line, convert them from list to char[][], and display their contents to the user.

I'm trying to practice exception-handling and javadocs, so I'm not interested in using a library. Also, this will be part of a larger program, hence the conversions happening, but I just want to focus on the I/O part for now. All comments are welcome!

public class Utilities {
    /**
     * This class must not be instantiated.
     */

    private Utilities() {}

    /**
     * Reads a file and returns a <code>List<String></code>.
     *
     * @param file path to the file
     * @return a <code>List<String></code> containing every line from the file
     * @throws AccessDeniedException if access rights are insufficient
     * @throws EOFException if the file is empty
     * @throws IOException I/O exception
     * @throws IllegalArgumentException (unchecked) if the path string cannot be converted
     *         to a <code>Path</code>
     * @throws NoRegularFileException if the file is not a regular file
     * @throws NoSuchFileException if the file does not exist
     * @throws SecurityException (unchecked) In the case of the default provider,
     *         and a security manager is installed, the <code>checkRead</code> is invoked
     *         to check read access to the file.
     */

    public static List<String> readFile(String file)
            throws NoSuchFileException, EOFException, IOException {
        Path p = Paths.get(file);
        if (!Files.exists(p)) {
            throw new NoSuchFileException(file);
        } else if (!Files.isRegularFile(p)) {
            throw new NoRegularFileException(file);
        } else if (!Files.isReadable(p)) {
            throw new AccessDeniedException(file);
        } else if (Files.size(p) == 0) {
            throw new EOFException(file);
        }
        return Files.readAllLines(p);
    }

    /**
      * Converts a <code>List</code> of any type to a <code>char[][]</code>.
      * 
      * @param <T> the type of the element
      * @param l a <code>List</code>
      * @return a <code>char[][]</code>. If the <code>List</code> is <code>null</code>,
      *         returns <code>null</code>. If an element of the <code>List</code> is
      *         <code>null</code>, the corresponding array is set to <code>null</code>.
      */

     public static <T> char[][] listToCharArray(List<T> l) {
         if (l == null) {
             return null;
         }

         char[][] arr = new char[l.size()][];
         {
             int i = 0;
             for (T t : l) {
                 if (t == null) {
                     arr[i++] = null;
                 } else {
                     arr[i++] = t.toString().toCharArray();
                 }
             }
          }
          return arr;
      }

      /**
       * Displays a <code>char[][]</code>.
       *
       * @param arr a <code>char[][]</code>
       */

      public static void display(char[][] arr) {
          for (char[] line : arr) {
              for (char c : line) {
                  System.out.print(c);
              }
              System.out.println();
          }
      }
  }

  class NoRegularFileException extends FileSystemException {
      public NoRegularFileException(String file) {
          super(file);
      }
  }

public class MyProgram {
    public static void main(String[] args) {
        try {
            if (args.length != 2) {
                System.err.println("The proper use is: java MyProgram file1.txt file2.txt");
                return;
            }

            List<List<String>> files = new ArrayList<>();
            for (String s : args) {
                try {
                    files.add(Utilities.readFile(s));
                } catch (NoSuchFileException e) {
                    System.err.printf("File %s does not exist%n", e.getMessage());
                    return;
                } catch (NoRegularFileException e) {
                    System.err.printf("File %s is not a regular file%n", e.getMessage());
                    return;
                } catch (AccessDeniedException e) {
                    System.err.printf(
                "Access rights are insufficient to read file %s%n", e.getMessage()
                    );
                    return;
                } catch (EOFException e) {
                    System.err.printf("File %s is empty%n", e.getMessage());
                    return;
                }
             }

             char[][] file1 = Utilities.listToCharArray(files.get(0));
             char[][] file2 = Utilities.listToCharArray(files.get(1));
             Utilities.display(file1);
             Utilities.display(file2);
         } catch (Exception e) {
             System.err.printf("Error: %s%n", e.getMessage());
         }
     }
}
\$\endgroup\$
4
  • \$\begingroup\$ Why are you converting the files which are List<String> into char[][]? You could just println all Strings in a list. \$\endgroup\$
    – ASA
    May 9, 2016 at 12:28
  • \$\begingroup\$ @Traubenfuchs Later in my program, the char[][] will be more convenient to use than a List<String> so I'll be printing from the char[][]. \$\endgroup\$
    – kevinlelo
    May 9, 2016 at 12:32
  • \$\begingroup\$ What is NoRegularFileException? I can't find that class in the Java API. \$\endgroup\$
    – Tunaki
    May 9, 2016 at 12:39
  • \$\begingroup\$ @Tunaki sorry I forgot to include it! \$\endgroup\$
    – kevinlelo
    May 9, 2016 at 12:41

1 Answer 1

2
\$\begingroup\$

It's been a while since I asked this question and I've improved this part of the code, so I'm posting it as an answer.

Edit: To answer rolfl's concerns, the main improvement to the first version of the code is the removal of the wrapper function around Files.readAllLines.

It felt redundant since it is already a wrapper around the verbose way of reading files and it is just two lines of code. Also, the point of that wrapper function was to throw more specific exceptions, but I later found out that Files.readAllLines already throws these exceptions, even though it is not specified in the documentation.

In the end, the result is a more straightforward code, where exception handling is simpler and exceptions are caught closer to the point they can be dealt with.

package project;

/**
 * This class consists exclusively of static utility methods.
 */
final class Utilities {
    /**
     * This class must not be instantiated.
     */
    private Utilities() {
        throw new IllegalStateException("This class must not be instantiated");
    }

    /**
     * Converts a list of any type to a two-dimensional (2D) {@code char}
     * array.
     * 
     * <p>The 2D array will contain all of the elements in the list as internal
     * arrays, in proper sequence from first to last element. If an element
     * of the list is {@code null}, the corresponding internal array is set to
     * {@code null}.
     * 
     * <p>The elements are converted to strings by
     * {@link java.lang.Object#toString() toString()} internally, so the method
     * should be properly overridden.
     * 
     * @param   <T>
     *          the type of the element
     * @param   l
     *          a list to be converted
     * @return  a {@code char[][]} containing the elements of the list. If
     *          {@code l} is {@code null}, returns {@code null}.
     * @see     java.util.ArrayList#toArray()
     */
    private static <T> char[][] listToCharArray(List<T> l) {
        if (l == null) {
            return null;
        }

        char[][] arr = new char[l.size()][];
        {
            int i = 0;
            for (T t : l) {
                if (t == null) {
                    arr[i++] = null;
                } else {
                    arr[i++] = t.toString().toCharArray();
                }
            }
        }
        return arr;
    }

    /**
     * Displays a <code>char[][]</code>.
     *
     * @param arr a <code>char[][]</code>
     */
    public static void display(char[][] arr) {
        for (char[] line : arr) {
            for (char c : line) {
                System.out.print(c);
            }
            System.out.println();
        }
        System.out.println();
    }
}

package project;

import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

final class MyProgram {
    /**
     * This class must not be instantiated.
     */
    private MyProgram() {
        throw new IllegalStateException("This class must not be instantiated");
    }

    public static void main(String[] args) throws Exception {
        if (args.length != Config.NUMBER_OF_FILES) {
            System.err.println(
                "The proper use is: java MyProgram file1 file2"
            );
            return;
        }

        List<List<String>> files = new ArrayList<>(args.length);
        for (int i = 0; i < args.length; i++) {
            try {
                Path p = Paths.get(args[i]);
                files.add(Files.readAllLines(p));
                if (files.get(i).isEmpty()) {
                    System.err.printf("File %1s is empty%n", args[i]);
                    return;
                }
            } catch (NoSuchFileException e) {
                System.err.printf("File %1s does not exist%n", e.getMessage());
                return;
            } catch (AccessDeniedException e) {
                System.err.printf(
                    "Access rights are insufficient to read file %1s%n",
                    e.getMessage()
                );
                return;
            } catch (IOException e) {
                System.err.printf(
                    "An error occurred while reading file %1s%n",
                    args[i]
                );
                return;
            }
        }
        char[][] file1 = Utilities.listToCharArray(files.get(0));
        char[][] file2 = Utilities.listToCharArray(files.get(1));
        Utilities.display(file1);
        Utilities.display(file2);         
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Nice self-answer. \$\endgroup\$
    – rolfl
    Jul 20, 2016 at 11:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.