// CertDataBase.java
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html

// The source code is jointly developed by W3C and some of its members
// participating in the Digital Signature Initiative.  The authors are:
//    Mark Champine, HP, <champine@apollo.hp.com>
//    Yang-hua Chu, MIT/W3C, <yhchu@w3.org>
//    Vasanthan Dasan, Sun, <vasanthan.dasan@central.sun.com>
//    Peter Lipp, University of Technology, Graz <plipp@iaik.tu-graz.ac.at>
//    Andreas Sterbenz, U. of Technology, Graz <sterbenz@iaik.tu-graz.ac.at>

package w3c.www.dsig;

import java.security.*;
import java.util.*;
import w3c.www.dsig.*;

/**
 * This class implements a simple database of identity certificates
 * mapping names to certificates.
 * It can be used to retrieve all available certificates
 * for an individual with a given name. Certificates follow the
 * standard java.security.Certificate API.
 * <P>
 * This class cannot be instantiated, all of its methods are static.
 * Extension mechanisms that allow to replace the simple base
 * class by one that indeed queries a database are yet to be
 * designed.
 *
 * @author Andreas Sterbenz
 * @version 1.0 (last modified 18-December-1997)
 */
public final class CertDatabase
{
  /**
   * Flag indicating debug mode. Internal use only.
   */
  private static boolean DEBUG = false;

  /**
   * Hashtable for the database. The keys in the hashtable are the
   * names of the certificate's subjects, the elements the keys map
   * to are vectors containg the certificate(s) of that individual.
   */
  private static Hashtable certs;

  static {
    initialize();
  }

  private static void initialize()
  {
    certs = new Hashtable();
  }

  /**
   * Prevent instantiation of the class using a compiler generated
   * default constructor by declaring a private constructor.
   */
  private CertDatabase()
  {
    super();
  }

  /**
   * Reset the database. It will contain no certificates after this
   * method is invoked. Mainly for debugging purposes.
   */
  public static void reset()
  {
    certs = new Hashtable();
  }

  /**
   * Print the names of each individual in the database and the number
   * certificate each of them has.
   */
  public static void printDatabase()
  {
    System.out.println("Currently known individuals:");
    for (Enumeration e = certs.keys() ; e.hasMoreElements() ;) {
      String name = (String)e.nextElement();
      Vector ncerts = (Vector)certs.get(name);
      System.out.println(name + ": " + ncerts.size() + " certificates");
    }
  }

  /**
   * Add a certificate to the database. Return true if the certificate
   * was successfully added or false otherwise
   * (i.e. it was already present).
   */
  public static boolean addCert(Certificate cert)
  {
    String name = cert.getPrincipal().getName();
    Vector ncerts = (Vector)certs.get(name);
    if( ncerts == null ) {
      ncerts = new Vector(1, 2);
      certs.put(name, ncerts);
    }
    for( Enumeration e = ncerts.elements() ; e.hasMoreElements() ;) {
      Certificate t = (Certificate)e.nextElement();
      if( t.equals(cert) ) return false;
    }
    ncerts.addElement(cert);
    return true;
  }

  /**
   * Removes the certificate at position index for the invidual name from
   * the database.
   * @exception ArrayIndexOutOfBoundsException if the specified certficate
   * does not exist.
   */
  public static void removeCert(String name, int index) throws ArrayIndexOutOfBoundsException
  {
    Vector ncerts = (Vector)certs.get(name);
    if( ncerts == null ) throw new ArrayIndexOutOfBoundsException();
    ncerts.removeElementAt(index);
  }

  /**
   * Removes the certificate at position index for the invidual name from
   * the database.
   * Shorthand for <CODE>removeCert(pr.getName(), index);</CODE>
   * @exception ArrayIndexOutOfBoundsException if the specified certficate
   * does not exist.
   */
  public static void removeCert(Principal pr, int index) throws ArrayIndexOutOfBoundsException
  {
    removeCert(pr.getName(), index);
  }

  /**
   * Remove a certificate from the database. Return true if it
   * is was successfully removed, false otherwise (i.e. certificate
   * was not present in the database).
   */
  public static boolean removeCert(Certificate cert)
  {
    String name = cert.getPrincipal().getName();
    Vector ncerts = (Vector)certs.get(name);
    if( ncerts == null ) {
      return false;
    }
    int n = ncerts.size();
    for( int i=0; i<n; i++ ) {
      if( cert.equals(ncerts.elementAt(i)) ) {
        ncerts.removeElementAt(i);
        return true;
      }
    }
    return false;
  }

  /**
   * Return an Enumeration of the names of all individuals in the
   * database.
   */
  public static Enumeration certificates()
  {
    return certs.keys();
  }

  /**
   * Return all certificates for the named individual. If no certificates
   * are available, this returns an array of size 0.
   */
  public static Certificate[] certForName(String name)
  {
    Vector ncerts = (Vector)certs.get(name);
    if( ncerts == null ) {
      return new Certificate[0];
    }
    Certificate[] ncertarray = new Certificate[ncerts.size()];
    ncerts.copyInto(ncertarray);
    return ncertarray;
  }

  /**
   * Return all certificates for the named individual.
   * Shorthand for <CODE>certForName(pr.getName())</CODE>
   */
  public static Certificate[] certForName(Principal pr)
  {
    return certForName(pr.getName());
  }

  /**
   * Return the certificate at position index for the given individual.
   * @exception ArrayIndexOutOfBoundsException if the given index is not valid.
   */
  public static Certificate certForName(String name, int index) throws ArrayIndexOutOfBoundsException
  {
    return certForName(name)[index];
  }

  /**
   * Return the certificate at position index for the given individual.
   * Shorthand for <CODE>certForName(pr.getName(), index)</CODE>.
   * @exception ArrayIndexOutOfBoundsException if the given index is not valid.
   */
  public static Certificate certForName(Principal pr, int index) throws ArrayIndexOutOfBoundsException
  {
    return certForName(pr.getName(), index);
  }
}
