// Hashregistry.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 w3c.tools.codec.*;
import java.util.*;
import java.security.*;
import java.io.*;

/**
 * The HashRegistry class maintains a database of the DSig message digest
 * algorithms installed on the system. Classes implementing DSig hash algorithms
 * follow the standard <CODE>java.security.MessageDigest</CODE> API. The only difference
 * between using this class the querying the java.security API is that
 * classes can be retrieved
 * here via their identifying URLs as defined in the DSig specification, as
 * opposed to their short names via the standard Java Security API.
 * <P>
 * When this class is loaded, it automatically searches the default Java
 * security provider for the algorithms MD5 and SHA-1 and installs them if they
 * are present (they normally are, as they are part of the Sun provider).
 * Classes in this registry database do not need to be installed as a
 * java.security provider, although they can be and usually will be.
 * <P>
 * NOTE - Limitations of this class: only one implementation of each
 * algorithm can be in the database at a time.
 *
 * @author Andreas Sterbenz
 * @version 1.0beta (last modified 19-December-1997)
 */
public final class HashRegistry
{
  /**
   * Flag indicating debug mode. Internal use only.
   */
  private final static boolean DEBUG = false;

  /**
   * The URL defined to indentify the MD5 hashing algorithm.
   * Currently this is 
   * <TT>http://www.w3.org/TR/1998/REC-DSig-label/MD5-1_0</TT>.
   */
  public final static String MD5URL = 
  "http://www.w3.org/TR/1998/REC-DSig-label/MD5-1_0";

  /**
   * The URL defined to indentify the SHA1 hashing algorithm.
   * Currently this is 
   * <TT>http://www.w3.org/TR/1998/REC-DSig-label/SHA1-1_0</TT>.
   */
  public final static String SHA1URL = 
  "http://www.w3.org/TR/1998/REC-DSig-label/SHA1-1_0";

  /**
   * Hashtable mapping URL names to class objects.
   */
  private static Hashtable algorithms;

  static {
    initialize();
  }

  /**
   * Initialize function to add a few algorithms by default. Called
   * by the static initializer of this class.
   */
  private static void initialize()
  {
    algorithms = new Hashtable();
    try {
      MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
      addAlgorithm(SHA1URL, sha1.getClass());
    } catch( NoSuchAlgorithmException e) {
      if(DEBUG) System.err.println("Could not find algorithm SHA-1!");
    }
    try {
      MessageDigest md5 = MessageDigest.getInstance("MD5");
      addAlgorithm(MD5URL, md5.getClass());
    } catch( NoSuchAlgorithmException e) {
      if(DEBUG) System.err.println("Could not find algorithm MD5!");
    }
  }

  /**
   * Return the number of installed algorithms.
   */
  public static int size()
  {
    return algorithms.size();
  }

  /**
   * Return an enumeration of the URL names of the currently
   * installed algorithms.
  */
  public static Enumeration algorithms()
  {
    return algorithms.keys();
  }

  /**
   * Print the list of currently installed hashing algorithms and the
   * names of the classes that implement them on stdout.
   */
  public static void printAlgorithms()
  {
    System.out.println("Currently installed hash algorithms:");
    for (Enumeration e = algorithms() ; e.hasMoreElements() ;) {
      String name = (String)e.nextElement();
      System.out.println(name + " -> " +((Class)algorithms.get(name)).getName());
    }
  }

  /**
   * Add a new hashing algorithm, identified via name, implemented by the
   * class clazz.
   * Returns true is successful, false otherwise (algorithm already present
   * or the class represented by the clazz object does not implement a
   * message digest algorithm).
   */
  public static boolean addAlgorithm(String name, Class clazz)
  {
    try {
      if( clazz.newInstance() instanceof MessageDigest ) {
        return algorithms.put(name, clazz) == null;
      } else {
        return false;
      }
    } catch( Exception e ) {
      return false; // throw an exception ?
    }
  }

  /**
   * Find out if the specified algorithm is installed in the
   * registry.
   */
  public static boolean isInstalled(String name)
  {
    return algorithms.get(name) != null;
  }

  /**
   * Remove the named algorithm from the registry.
   * Returns true if successful, false if the algorithm was not installed.
   */
  public static boolean removeAlgorithm(String name)
  {
    return algorithms.remove(name) == null;
  }

  /**
   * Get an instance of a class implementing the hash algorithm name.
   * Name is the URL identifying the algorithm.
   * @exception NoSuchAlgorithmException if the algorithm cannot be found.
   */
  public static MessageDigest getInstance(String name)
    throws NoSuchAlgorithmException
  {
    try {
      return (MessageDigest)((Class)algorithms.get(name)).newInstance();
    } catch( Exception e ) {
      throw new NoSuchAlgorithmException();
    }
  }

  /**
   * Return the Class object of the class implementing the algorithm
   * name.
   * @exception NoSuchAlgorithmException if the algorithm is not installed.
   */
  public static Class getImplementor(String name) throws NoSuchAlgorithmException
  {
    if( isInstalled(name) ) {
      return (Class)algorithms.get(name);
    } else {
      throw new NoSuchAlgorithmException();
    }
  }

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

}
