// (c) MIT 2003.  All rights reserved.

////////////////////////////////////////////////////////////////////////////////
//
// FILE:	DynamicConstructor.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
// DATE:	Jul 98
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:     Dynamic class creation
//
// HISTORY:     Jul 98  archon  created
//
//              Mar 99  archon
//              Add null dynamic constructor support.
//
////////////////////////////////////////////////////////////////////////////////

package rec.util;

import java.lang.reflect.*;
import java.io.*;

public final class DynamicConstructor
{
  public static DynamicConstructor create( Class classClass )
  {
    DynamicConstructor dc = null;

    try {
      dc = new DynamicConstructor( classClass );
    }
    catch( DynamicConstructorException e ) {
      return( new DynamicConstructor() );
    }

    return( dc );
  }

  public static DynamicConstructor create( Class classClass,
					   Class[] paramTypes )
  {
    DynamicConstructor dc = null;

    try {
      dc = new DynamicConstructor( classClass, paramTypes );
    }
    catch( DynamicConstructorException e ) {
      return( new DynamicConstructor() );
    }

    return( dc );
  }

  public static DynamicConstructor create( Class classClass,
					   String[] typeNames )
  {
    DynamicConstructor dc = null;

    try {
      dc = new DynamicConstructor( classClass, typeNames );
    }
    catch( DynamicConstructorException e ) {
      return( new DynamicConstructor() );
    }

    return( dc );
  }

  public static DynamicConstructor create( String className )
  {
    DynamicConstructor dc = null;

    try {
      dc = new DynamicConstructor( className );
    }
    catch( DynamicConstructorException e ) {
      return( new DynamicConstructor() );
    }

    return( dc );
  }

  public static DynamicConstructor create( String className,
					   Class[] paramTypes )
  {
    DynamicConstructor dc = null;

    try {
      dc = new DynamicConstructor( className, paramTypes );
    }
    catch( DynamicConstructorException e ) {
      return( new DynamicConstructor() );
    }

    return( dc );
  }

  public static DynamicConstructor create( String className,
					   String[] typeNames )
  {
    DynamicConstructor dc = null;

    try {
      dc = new DynamicConstructor( className, typeNames );
    }
    catch( DynamicConstructorException e ) {
      return( new DynamicConstructor() );
    }

    return( dc );
  }

  //////////////////////////////////////////////////////////////////////////////

  // Use for creating the null dynamic constructor.
  private DynamicConstructor()
  {
    m_isNull = true;
  }

  private DynamicConstructor( Class classClass )
  {
    initialize( classClass );
  }

  private DynamicConstructor( Class classClass,
			      Class[] paramTypes )
  {
    if( paramTypes == null ) {
      initialize( classClass );
      return;
    }

    initialize( classClass, paramTypes );
  }

  private DynamicConstructor( Class classClass,
			      String[] typeNames )
  {
    if( typeNames == null ) {
      initialize( classClass );
      return;
    }

    Class[] ct = classesForNames( typeNames );

    if( ct == null )
      throw( new DynamicConstructorException(
	"unable to resolve type names" ) );

    initialize( classClass, ct );
  }

  private DynamicConstructor( String className )
  {
    Class c = classForName( className );

    if( c == null )
      throw( new DynamicConstructorException(
	"unable to resolve class name: " + className ) );

    initialize( c );
  }

  private DynamicConstructor( String className,
			      Class[] paramTypes )
  {
    Class c = classForName( className );

    if( c == null )
      throw( new DynamicConstructorException(
	"unable to resolve class name: " + className ) );

    if( paramTypes == null ) {
      initialize( c );
      return;
    }

    initialize( c, paramTypes );
  }

  private DynamicConstructor( String className,
			      String[] typeNames )
  {
    Class c = classForName( className );

    if( c == null )
      throw( new DynamicConstructorException(
	       "unable to resolve class name: " + className ) );

    if( typeNames == null ) {
      initialize( c );
      return;
    }

    Class[] ct = classesForNames( typeNames );

    if( ct == null )
      throw( new DynamicConstructorException(
	       "unable to resolve type names" ) );

    initialize( c, ct );
  }

  //////////////////////////////////////////////////////////////////////////////

  private void initialize( Class classClass )
  {
    m_isNull = false;

    initialize( classClass, null );
  }

  private void initialize( Class classClass, Class[] paramTypes )
  {
    try {
      m_Constructor = classClass.getConstructor( paramTypes );
    }
    catch( NoSuchMethodException e ) {
      throw( new DynamicConstructorException(
	"no such constructor for " + classClass.getName() ) );
    }

    m_isDefaultConstructor = (paramTypes == null) ? true : false;

  }

  //////////////////////////////////////////////////////////////////////////////

  public Object create()
  {
    if( m_isNull )
      return( null );

    if( !m_isDefaultConstructor ) {
      Console.writeln( "is not a default constructor" );
      return( null );
    }

    Object o = null;

    try {
      o = m_Constructor.newInstance( null );
    }
    // print out the stack trace of any exceptions thrown by the
    // constructor but still return null
    catch( InvocationTargetException ite ) {
      Class cls = m_Constructor.getDeclaringClass();
      Console.writeln( "*****Exception in constructor: " + cls );
      Console.writeln( ite.getTargetException().toString() );
      ite.getTargetException().printStackTrace();
      Console.writeln( "***** End Exception *****" );
      return( null );
    }
    catch( Exception e ) {
      Console.writeln( "unable to execute default constructor" );
      return( null );
    }

    return( o );
  }

  public Object create( Object[] paramValues )
  {
    if( m_isNull )
      return( null );

    if( m_isDefaultConstructor ) {
      Console.writeln( "is a default constructor" );
      return( null );
    }

    Object o = null;

    try {
      o = m_Constructor.newInstance( paramValues );
    }
    // print out the stack trace of any exceptions thrown by the
    // constructor but still return null
    catch( InvocationTargetException ite ) {
      Class cls = m_Constructor.getDeclaringClass();
      Console.writeln( "*****Exception in constructor: " + cls );
      Console.writeln( ite.getTargetException().toString() );
      Console.writeln( "***** End Exception *****" );
      return( null );
    }
    catch( Exception e ) {
      Console.writeln( "unable to execute constructor with given parameters" );
      return( null );
    }

    return( o );
  }

  public boolean isNull()
  {
    return( m_isNull );
  }

  //////////////////////////////////////////////////////////////////////////////

  private static Class classForName( String className )
  {
    Class c = null;

    try {
      c = Class.forName( className );
    }
    catch( ClassNotFoundException e ) {
      Console.writeln( "unknown class: " + className );
      return( null );
    }

    return( c );
  }

  private static Class[] classesForNames( String[] classNames )
  {
    Class[] classTypes = new Class[classNames.length];

    int i = 0;

    try {
      for( i = 0; i < classTypes.length; ++i ) {
	classTypes[i] = Class.forName( classNames[i] );
      }
    }
    catch( ClassNotFoundException e ) {
      Console.writeln( "unknown class: " + classNames[i] );
      return( null );
    }

    return( classTypes );
  }


  //////////////////////////////////////////////////////////////////////////////

  private class DynamicConstructorException
    extends RuntimeException
  {
    public DynamicConstructorException()
    {
    }

    public DynamicConstructorException( String msg )
    {
      super( msg );
    }
  }

  //////////////////////////////////////////////////////////////////////////////

  private Constructor m_Constructor;
  private boolean m_isDefaultConstructor;
  private boolean m_isNull;
}

////////////////////////////////////////////////////////////////////////////////
