// SignatureRegistry.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.util.*;
import java.security.*;
import java.io.*;
import w3c.tools.codec.*;
import w3c.www.dsig.*;
import w3c.crypto.us.dsig.*;

/**
 * The SignatureRegistry class maintains a database of the DSig signature
 * suites installed on the system.
 * They can be retrieved here via there identifying URLs as defined in the
 * DSig specification. When this class is loaded, it automatically searches
 * the default Java security provider for the algorithms DSA
 * and installs it if presents (it normally is, as it is part of the
 * Sun provider).
 * <P>
 * Limitations of this class: only one implementation of each algorithm can
 * be in the database at one time.
 *
 * @author Andreas Sterbenz
 * @author Yang-hua Chu
 * @version 1.0beta (last modified 30-sep-1997)
 */
public final class SigSuiteRegistry
{
  /**
   * Flag indicating debug mode. Internal use only.
   */
  private final static boolean DEBUG = false;

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

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

  /**
   * The URL defined to indentify the RSA-SHA1 Signature algorithm.
   * Currently this is 
   * <TT>http://www.w3.org/PICS/DSig/RSA-SHA1-1_0.html</TT>.
   */
  public final static String RSASHA1URL =
  "http://www.w3.org/PICS/DSig/RSA-SHA1-1_0.html";

  /**
   * 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 {
      SignatureSuite dss = new SigSuiteDSS();
      addAlgorithm(DSSURL, dss.getClass());
    } catch( NoSuchAlgorithmException e) {
      if(DEBUG) System.err.println("Could not find algorithm DSS!");
    }
    try {
      SignatureSuite rsamd5 = new SigSuiteRSAMD5();
      addAlgorithm(RSAMD5URL, rsamd5.getClass());
    } catch( NoSuchAlgorithmException e) {
      if (DEBUG) System.err.println("Could not find algorithm RSA/MD5!");
    }
    try {
      SignatureSuite rsasha1 = new SigSuiteRSASHA1();
      addAlgorithm(RSASHA1URL, rsasha1.getClass());
    } catch( NoSuchAlgorithmException e) {
      if (DEBUG) System.err.println("Could not find algorithm RSA/SHA1!");
    }
  }

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

  /**
   * Return the number of installed signature suites.
   */
  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 Signature algorithms and the classes
   * that implement them to stdout.
   */
  public static void printAlgorithms()
  {
    System.out.println("Currently installed Signature algorithms:");
    for (Enumeration e = algorithms() ; e.hasMoreElements() ;) {
      String name = (String)e.nextElement();
      System.out.println(name + " -> " +((Class)algorithms.get(name)).getName());
    }
  }

  /**
   * Add a new signature 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
   * signature suite.
   */
  public static boolean addAlgorithm(String name, Class clazz)
  {
    try {
      if( clazz.newInstance() instanceof SignatureSuite ) {
        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 Signature algorithm name.
   * Name is the URL identifying the algorithm.
   * @exception NoSuchAlgorithmException if the corresponding signature
   * algorithm cannot be found
   */
  public static SignatureSuite getInstance(String name)
    throws NoSuchAlgorithmException
  {
    try {
      return (SignatureSuite)((Class)algorithms.get(name)).newInstance();
    } catch( Exception e ) { // NullPointerException, no others exceptions should be thrown here
      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();
    }
  }
}
