ChengJade12.java

import java.lang.reflect.Array;
import java.util.NoSuchElementException;
import java.util.Scanner;

/**
 * This is an implementation of Assignment 12.
 * 
 * @author     Cheng Yu
 * @assignment ICS211 Homework TA12
 * @date       Dec 4, 2008
 * @bugs       None
 */
public class ChengJade12 {

  /**
   * The main entry point of the program.
   * 
   * @param args The command line arguments (not used).
   */
  public static void main(final String[] args) {
    final PhoneBook book = new PhoneBook();
    final Scanner scanner = new Scanner(System.in);

    // Loop until the user selects quit, but always show the menu of options.
    try {
      while (true) {

        // Display the menu.
        System.out.println("\nChoose one of the four options:\n");
        System.out.println("  1) Type 'quit' to exit the program.");
        System.out.println("  2) Type 'insert' to add a new contact.");
        System.out.println("  3) Type 'delete' to delete a contact.");
        System.out.println("  4) Type 'display' to show the phone book.");
        System.out.println("  5) Type a name to show a phone number.");
        System.out.print("\nYour option: ");
        final String input = scanner.nextLine();

        // Check for the quit command.
        if (input.equalsIgnoreCase("quit"))
          break;

        // Check for the insert command.
        if (input.equalsIgnoreCase("insert")) {
          System.out.print("\nPlease enter the name to add: ");
          final String name = scanner.nextLine();
          System.out.print("Please enter phone number:    ");
          final String number = scanner.nextLine();

          if (!validPhoneNumber(number))
            System.out.println("\nWarning: '" + number
                + "' does not look like a valid phone number.");

          // It is unlikely but possible that two different names will produce
          // the same hash code, but if they do it will cause a failure in this
          // implementation of the hash table.
          try {
            book.add(name, number);
          } catch (final RuntimeException e) {
            System.err.println("\nError: Cannot add '" + name
                + "' because another entry with a different\nname exists "
                + "in the hash table and produces the same hash code.");
            continue;
          }

          System.out.println("\nInserted '" + name + "' with phone number '"
              + number + "'.");
          continue;
        }

        // Check for the delete command.
        if (input.equalsIgnoreCase("delete")) {
          System.out.print("\nPlease enter the name to remove: ");
          final String name = scanner.nextLine();
          try {
            book.remove(name);
          } catch (final NoSuchElementException e) {
            System.out.println("\nThe name '" + name + "' does not exist.");
            continue;
          }
          System.out.println("\nRemoved '" + name + "' from the phone book.");
          continue;
        }

        // Check for the display command.
        if (input.equalsIgnoreCase("display")) {
          System.out.println(String.format("\n%1$-10s%2$-20s%3$s", "#", "Name",
              "Phone Number"));
          System.out.print(book);
          continue;
        }

        // For all other cases, treat the input as a name to search for.
        final String number;
        try {
          number = book.getPhoneNumber(input);
        } catch (final NoSuchElementException e) {
          System.out.println("\nThe name '" + input + "' does not exist.");
          continue;
        }

        System.out.println("\n" + input + "'s phone number is " + number + ".");
      }
    } catch (final NoSuchElementException e) {
      // The user has canceled the program (probably with Ctrl-C. Exit normally.
    }

    System.exit(0);
  }


  /**
   * This method returns true if the specified phone number matches a typical
   * phone number pattern: "[#-[###-]]###-####".
   * 
   * @param number The phone number.
   * @return True indicates the phone number is valid looking.
   */
  private static boolean validPhoneNumber(final String number) {
    assert number != null;
    return number.matches("(\\d-)?(\\d{3}-)?\\d{3}-\\d{4}");
  }
}

/**
 * This class implements a generic hash table. The items of the hash table must
 * be comparable with each other.
 */
class HashTable<T extends Comparable<T>> {
  private T[] myTable = newArray(3);

  /**
   * This method adds an item to the hash table. In the event of a collision,
   * the hash table is resized. If the item already exists in the hash table,
   * then it is replaced. The item cannot be null.
   * 
   * @param o The item to add.
   */
  public void add(final T value) {
    if (value == null)
      throw new NullPointerException("value");
    final int index = getIndex(value, myTable.length);
    if (myTable[index] == null || myTable[index].compareTo(value) == 0) {
      myTable[index] = value;
      return;
    }
    // If two different names return the same hash code, there is no possible
    // way to rehash the table. For example, "2<" and "0z" return the same
    // hash code. If these two strings were added to the hash table, then the
    // rehash method would fail with a stack overflow due to infinite recursion.
    // Instead of failing in this way, we handle the condition more gracefully.
    if (myTable[index].hashCode() == value.hashCode())
      throw new RuntimeException();
    rehash(myTable.length * 2 + 1);
    add(value);
  }


  /**
   * This method searches for an item in the hash table. If the item is found,
   * the item is returned from the hash table. If the item is not found, then
   * null is returned.
   * 
   * @param o The item to find.
   * @return The item from the hash table or null if the item is not found.
   */
  public T find(final T value) {
    if (value == null)
      throw new NullPointerException("value");
    final int index = getIndex(value, myTable.length);
    if (myTable[index] == null || myTable[index].compareTo(value) != 0)
      return null;
    return myTable[index];
  }


  /**
   * This method removes an item from the hash table and returns true if
   * successful. If the item is not found, the method returns false.
   * 
   * @param o The item.
   * @return True indicates the item was removed.
   */
  public boolean remove(final T value) {
    if (value == null)
      throw new NullPointerException("value");
    final int index = getIndex(value, myTable.length);
    if (myTable[index] == null || myTable[index].compareTo(value) != 0)
      return false;
    myTable[index] = null;
    return true;
  }


  /**
   * This method returns the string representation of the class.
   * 
   * @return The string representation of the class.
   */
  @Override
/*  public String toString() {
    final StringBuilder builder = new StringBuilder();
    int j = 0;
    for (final T element : myTable)
      if (element != null)
        builder.append(String.format("%1$-10s%2$s\n", Integer.valueOf(++j),
            element));
    builder.append('\n' + "The current table size is: " + myTable.length + '\n');
    return builder.toString();
  }*/
  public String toString() {
    final StringBuilder builder = new StringBuilder();
    int j = 0;
    for (final T element : myTable)
      if (element != null) {
        builder.append(Integer.valueOf(++j) + "\t");
        builder.append(element + "\n");
      }
    return builder.toString();
  }


  /**
   * This method returns a hash code index for the specified object. The value
   * returned is some number between zero and one less than the specified size.
   * 
   * @param o The object.
   * @param tableSize The table size.
   * @return The hash code index.
   */
  private int getIndex(final T value, final int tableSize) {
    assert value != null;
    assert tableSize > 0;
    return Math.abs(value.hashCode()) % tableSize;
  }


  /**
   * Creates and returns an array of a specified length for the generic type.
   * 
   * @param length The length of the array.
   * @return A new array of the generic type.
   */
  @SuppressWarnings("unchecked")
  private T[] newArray(final int length) {
    assert length >= 0;
    return (T[])Array.newInstance(Comparable.class, length);
  }


  /**
   * This method rehashes the hash table for a new size. If there are collisions
   * while rehashing the table, then the method executes recursively with a
   * larger size.
   * 
   * @param newSize The new size of the hash table.
   */
  private void rehash(final int newSize) {
    assert newSize > myTable.length;
    final T[] newTable = newArray(newSize);
    for (final T value : myTable) {
      if (value == null)
        continue;
      final int index = getIndex(value, newSize);
      if (newTable[index] == null) {
        newTable[index] = value;
        continue;
      }
      rehash(newSize * 2 + 1);
      return;
    }
    myTable = newTable;
  }
}

/**
 * This class implements information that is entered into the telephone book.
 */
class PersonInfo implements Comparable<PersonInfo> {
  private final String myName;
  private final String myNumber;


  /**
   * This method compares two instances of class. The comparison is based only
   * on the name and not the number.
   * 
   * @return 1 if greater, 0 if equal, and -1 if less than the specified value.
   */
  public int compareTo(final PersonInfo person) {
    return person == null ? 1 : myName.compareToIgnoreCase(person.getName());
  }


  /**
   * This constructor initializes the class data so that it is suitable for
   * searching for entries. The number is initializes to zero.
   * 
   * @param name The name of the entry.
   */
  public PersonInfo(final String name) {
    this(name, "");
  }


  /**
   * This constructor initializes the class data.
   * 
   * @param name The name of the entry.
   * @param number The phone number of the entry.
   */
  public PersonInfo(final String name, final String number) {
    if (name == null)
      throw new NullPointerException("name");
    if (number == null)
      throw new NullPointerException("number");
    myName = name;
    myNumber = number;
  }


  /**
   * This method returns the name.
   * 
   * @return The name.
   */
  public String getName() {
    return myName;
  }


  /**
   * This method returns the number.
   * 
   * @return The number.
   */
  public String getNumber() {
    return myNumber;
  }


  /**
   * This method returns the hash code for the class. The code is based only on
   * the name and not the number.
   * 
   * @return The hash code.
   */
  @Override
  public int hashCode() {
    return myName.hashCode();
  }


  /**
   * This method returns the string representation of the class.
   * 
   * @return The string representation of the class.
   */
  @Override
  public String toString() {
    return String.format("%1$-20s%2$s", myName, myNumber);
  }
}

/**
 * This class implements a phone book.
 */
class PhoneBook {
  private final HashTable<PersonInfo> book = new HashTable<PersonInfo>();

  /**
   * This method adds an item to the phone book.
   * 
   * @param name The name to add.
   * @param number The number to add.
   */
  public void add(final String name, final String number) {
    if (name == null)
      throw new NullPointerException("name");
    book.add(new PersonInfo(name, number));
  }


  /**
   * This method searches for an item in the telephone book. If the item is
   * found, then the phone number is returned. If the item is not found, then
   * NoSuchElementException is thrown.
   * 
   * @param name The name of the item to find.
   * @return The phone number or null if the item is not found.
   * @throws NoSuchElementException
   */
  public String getPhoneNumber(final String name) {
    if (name == null)
      throw new NullPointerException("name");
    final PersonInfo entryFound = book.find(new PersonInfo(name));
    if (entryFound == null)
      throw new NoSuchElementException();
    return entryFound.getNumber();
  }


  /**
   * This method removes an item (based on a name) from the phone book.
   * 
   * @param name The name of the item to remove.
   * @throws NoSuchElementException
   */
  public void remove(final String name) {
    if (name == null)
      throw new NullPointerException("name");
    if (!book.remove(new PersonInfo(name)))
      throw new NoSuchElementException();
  }


  /**
   * This method returns the string representation of the class.
   * 
   * @return The string representation of the class.
   */
  @Override
  public String toString() {
    return book.toString();
  }
}
Valid HTML 4.01 Valid CSS