ChengJade05.java

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.NoSuchElementException;
import java.util.Scanner;

/**
 * This class provides a main method used to get input from the user. This class
 * constructs an address book by calling the constructor with a string from the
 * command line that specifies the file name, prints the contents of the
 * database using toString, then accepts input from the user. The input is of
 * four kinds:
 * <ul>
 * <li>the string "quit", which exits the program.</li>
 * <li>the string "save", which saves the database back to the file from which
 * the database was originally loaded.</li>
 * <li>an email address containing '@', is a request to print all the entries
 * matching the given email address.</li>
 * <li>a name, which is an arbitrary string, means remove that name from the
 * list.</li>
 * </ul>
 *
 * @author     Cheng Yu
 * @assignment ICS211 TA Homework 5
 * @date       Oct 8, 2008
 * @bugs       None
 */
public class ChengJade05 {

  /**
   * The main entry point of the program.
   *
   * @param args
   *          The command line arguments.
   */
  public static void main(final String[] args) {

    // Check there is a path on the command line.
    if (args.length != 1) {
      System.err.println("Usage: ChengJade05 path");
      System.exit(1);
    }

    // Create a phone directory by calling the constructor and load the
    // contents from the specified file.
    try {
      final String path = args[0];
      final Directory directory = new Directory(path);

      // Start a loop that responds to user requests.
      while (true) {

        // Read the standard input using System.in.
        final Scanner scanner = new Scanner(System.in);

        // Print the contents of the database using toString of the directory
        // object.
        System.out.println("Directory Contents:\n");
        System.out.println(directory);

        // Print out the instructions.
        System.out.println("Choose one of the four options:\n");
        System.out.println("  1) Type 'quit' to exit the program.");
        System.out.println("  2) Type 'save' to save the database.");
        System.out.println("  3) Type an email address containing '@' to find matching entries.");
        System.out.println("  4) Type a name to remove.");
        System.out.print("\nYour option: ");
        System.out.flush();

        // Check there is a line ready to read from standard input, read the
        // line, and ignore blank lines.
        if (!scanner.hasNextLine())
          break;
        final String line = scanner.nextLine();
        if (line.length() == 0) {
          System.out.println("Ignoring blank line.\n");
          continue;
        }

        // If the user types 'quit' then stop looping and exit.
        if (line.compareToIgnoreCase("quit") == 0)
          break;

        // If the user types 'save' then re-save the address book to the
        // same file.
        if (line.compareToIgnoreCase("save") == 0) {
          directory.saveToFile(path);
          System.out.println("Done saving.\n");
          continue;
        }

        // If the user types a string containing '@' then find and
        // display the matching entries.
        if (line.contains("@")) {
          System.out.println(directory.findEmail(line));
          continue;
        }

        // The user entered a string. Remove all entries that have the
        // name and count the number of entries removed.
        final int count = directory.remove(line);
        if (count == 0) {
          System.out.println("No such entry to remove.\n");
          continue;
        }

        System.out.println("Done removing. The number of entries removed is "
            + count + "\n");
      }
    } catch (final ProgramException e) {

      // In case of any errors, display the message, and exit with a
      // failure.
      System.err.println(e.getMessage());
      System.exit(1);
    }

    // Return zero to let the eclipse debugger close happily.
    System.exit(0);
  }
}

/**
 * An address class.
 */
class Node {
  private final String myEmail;
  private final String myName;
  private Node         myNext;
  private final long   myPhone;


  /**
   * Initializes a new instance of the class.
   *
   * @param name
   *          The name stored in this class.
   * @param phone
   *          The phone number stored in this class.
   * @param email
   *          The email address stored in this class.
   */
  public Node(final String name, final long phone, final String email) {
    assert name != null;
    assert email != null;

    myName = name;
    myPhone = phone;
    myEmail = email;
    myNext = null;
  }


  /**
   * Compares this address node's email to another email. The result is an
   * integer compatible with other compareTo methods. The string comparisons are
   * case insensitive.
   *
   * @param email
   *          The other email.
   * @return An integer compatible with other compareTo methods.
   */
  public int compareEmailTo(final String email) {
    assert email != null;
    return myEmail.compareToIgnoreCase(email);
  }


  /**
   * Compares this address node to another address node. The comparison is made
   * between the names of the two nodes and no other fields. The result is an
   * integer compatible with other compareTo methods. The string comparisons are
   * case insensitive.
   *
   * @param other
   *          The other address.
   * @return An integer compatible with other compareTo methods.
   */
  public int compareTo(final Node other) {
    if (other == null)
      return 1;
    return compareTo(other.myName);
  }


  /**
   * Compares this address node to a name. The result is an integer compatible
   * with other compareTo methods. The string comparisons are case insensitive.
   *
   * @param name
   *          The other name.
   * @return An integer compatible with other compareTo methods.
   */
  public int compareTo(final String name) {
    assert name != null;
    return myName.compareToIgnoreCase(name);
  }


  /**
   * Returns the email address.
   *
   * @return The email address.
   */
  public String email() {
    return myEmail;
  }


  /**
   * Returns the name.
   *
   * @return The name.
   */
  public String name() {
    return myName;
  }


  /**
   * Returns the pointer to the next node.
   *
   * @return The pointer to the next node.
   */
  public Node next() {
    return myNext;
  }


  /**
   * Returns the phone number.
   *
   * @return The phone number.
   */
  public long phone() {
    return myPhone;
  }


  /**
   * Sets the pointer to the next node.
   *
   * @param next
   *          The next node.
   */
  public void setNext(Node next) {
    myNext = next;
  }


  /**
   * Returns the string representation of the class data.
   *
   * @return The string representation of the class data.
   */
  @Override
  public String toString() {
    return myName + '\n' + myPhone + '\n' + myEmail;
  }
}

/**
 * A directory class.
 */
class Directory {

  // Define the head node. The linked list is empty when the head node is null.
  private Node myHead = null;


  /**
   * Initializes a new instance of the class. The directory is read from the
   * specified file.
   *
   * @param path
   *          The path to the file used to initializes the database.
   * @throws ProgramException
   *           Thrown if there is a problem reading the file.
   */
  public Directory(final String path) throws ProgramException {
    assert path != null;

    // Open the file for reading with a scanner.
    FileReader file = null;
    try {
      file = new FileReader(path);
    } catch (FileNotFoundException e) {
      throw new ProgramException("Unable to open the file " + path, e);
    }
    final Scanner scanner = new Scanner(file);

    // Loop until there are no more lines. Create and add a node in each
    // iteration of the loop.
    while (scanner.hasNextLine()) {
      String phoneText = null;
      try {
        final String name = scanner.nextLine();
        phoneText = scanner.nextLine();
        final long phone = Long.parseLong(phoneText);
        final String email = scanner.nextLine();
        add(new Node(name, phone, email));

      } catch (NoSuchElementException e) {
        throw new ProgramException("Unexpected end of file " + path, e);

      } catch (NumberFormatException e) {
        assert phoneText != null;
        throw new ProgramException("Invalid phone number: " + phoneText, e);
      }
    }

    // Close the file immediately (do not wait for garbage collection).
    scanner.close();
    try {
      file.close();
    } catch (IOException e) {
      throw new ProgramException("Unable to close the file " + path, e);
    }
  }


  /**
   * Adds an address to the directory.
   *
   * @param nodeToAdd
   *          The new address entry to add to the directory.
   */
  public void add(final Node nodeToAdd) {
    assert nodeToAdd != null;

    // Check if the directory is empty or if this item belongs at the head
    // of the directory. If so, add the node at the head of the directory.
    final String name = nodeToAdd.name();
    assert name != null;
    if (myHead == null || myHead.compareTo(nodeToAdd) >= 0) {
      nodeToAdd.setNext(myHead);
      myHead = nodeToAdd;
      return;
    }

    // Loop over nodes until either the end of the directory is reached or
    // the node belongs alphabetically before the next node.
    Node nodeInList = myHead;
    while (nodeInList.next() != null && nodeInList.next().compareTo(nodeToAdd) < 0)
      nodeInList = nodeInList.next();

    nodeToAdd.setNext(nodeInList.next());
    nodeInList.setNext(nodeToAdd);
  }


  /**
   * Finds entries for the specified email address. Each item is put into a list
   * similar to the one returned by toString().
   *
   * @param email
   *          The email address.
   * @return A list of entries with the specified email address.
   */
  public String findEmail(final String email) {
    assert email != null;

    final StringBuilder builder = new StringBuilder();

    // Start from the head and loop over to the end of the directory.
    for (Node node = myHead; node != null; node = node.next()) {

      // If the email address is a match, add it to the result.
      if (node.compareEmailTo(email) == 0)
        builder.append(node.toString() + '\n');

    }

    // If nothing added to the result, return a message. Otherwise return
    // the contents.
    final String result = builder.toString();
    return (result.length() == 0) ? "No such email address found.\n" : result;
  }


  /**
   * Removes items from the directory with the specified name. If more than one
   * item is found, then all items are removed.
   *
   * @param name
   *          The name of the item to remove from the list.
   * @return The number of items removed.
   */
  public int remove(final String name) {
    assert name != null;

    // Do nothing if there are no items in the linked list.
    if (myHead == null)
      return 0;

    // Check to remove the head of the list. If the head is removed, use
    // recursion to search for other names to remove as well.
    if (myHead.compareTo(name) == 0) {
      myHead = myHead.next();
      return 1 + remove(name);
    }

    // Loop over the remaining items. Check each next node for a matching name,
    // and if the name is a match, then remove that node and use recursion to
    // search for other names to remove.
    for (Node node = myHead; node.next() != null; node = node.next()) {

      if (node.next().compareTo(name) == 0) {
        node.setNext(node.next().next());
        return 1 + remove(name);
      }

    }

    // Arriving here indicates no items were removed from the directory.
    return 0;
  }


  /**
   * Saves the contents of the directory the specified file. The items are saved
   * in alphabetical order based on the name.
   *
   * @param path
   *          The path to the file.
   * @throws ProgramException
   *           Thrown if there is a problem writing to the file.
   */
  public void saveToFile(final String path) throws ProgramException {
    assert path != null;

    try {
      // Open the file for writing, write the contents of the directory by
      // calling toString, and close the writer. Write nothing if the directory
      // is empty.
      final FileWriter writer = new FileWriter(path);

      if (myHead != null)
        writer.write(toString());

      writer.close();

    } catch (final IOException e) {
      throw new ProgramException("Error writing to file " + path, e);
    }
  }


  /**
   * Returns the string representation of the class data.
   *
   * @returns The string representation of the class data.
   */
  @Override
  public String toString() {

    // Return a special message for no items in the directory.
    if (myHead == null)
      return "The database is empty.\n";

    // Loop over each item in the directory. Each iteration, build a
    // list for each directory entry, and then return that list as a string.
    final StringBuilder builder = new StringBuilder();

    for (Node node = myHead; node != null; node = node.next())
      builder.append(node.toString() + "\n");

    return builder.toString();
  }
}

/**
 * An exception class that is thrown and caught by the program.
 */
@SuppressWarnings("serial")
class ProgramException extends Exception {

  /**
   * Initializes a new instance of the class.
   *
   * @param message
   *          The message.
   */
  public ProgramException(final String message) {
    super(message);
  }


  /**
   * Initializes a new instance of the class.
   *
   * @param message
   *          The message.
   * @param cause
   *          The cause.
   */
  public ProgramException(final String message, Throwable cause) {
    super(message, cause);
  }
}
Valid HTML 4.01 Valid CSS