/********************************************************************
 Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.fw.util.builder;

// Java classes

// EDS Classes
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import gov.va.med.fw.service.AbstractComponent;
import gov.va.med.fw.util.ReflectionException;
import gov.va.med.fw.util.Reflector;

/**
 * An abstract builder class that implements a Builder interface to encapsulate
 * how an object is built from a generic input data. This class is designed as
 * an abstract class to force a derived class to provide its own implementation
 * of constructing an object from a specific data type in a processBuild method.
 * 
 * @author Vu Le
 * @version 1.0
 * @since 1.0
 */
public abstract class AbstractBuilder extends AbstractComponent implements Builder {

   
    private static final long serialVersionUID = -1370346746640850300L;
    
    /**
    * An instance of a builder class
    */
   protected AbstractBuilder() {
      super();
   }

   /**
    * Constructs an object from a generic input data. This method uses an input
    * object's class name to look up a formatter class that knows how to format
    * an input object's data into a valid form. This method gets a formatter
    * class from a formatter factory class then pass a formatter and an input
    * object to a following method: <code>
    * <pre>
    *   public Object build( Object input, Formatter formatter )
    * </pre>
    * </code>
    * @param input data from which an object is constructed
    * @throws BuilderException Thrown if failed to construct an object
    * @return Object a constructed object
    */
   public Object build(Object input) throws BuilderException {

      if (input == null) {
         throw new BuilderException("Invalid null input object");
      }

      if (input instanceof Object[])
      {
          try {
              return this.invokeBuild( (Object[])input );
          }
          catch( NoSuchMethodException e ) {
             if( logger.isDebugEnabled() ) {
                logger.debug( "Could not invoke method from unpackaged object array types" );
             }
          }
          catch( InvocationTargetException e ) {
             Throwable cause = e.getCause();
             throw new BuilderException( cause.getMessage(), cause );
          }
          catch( Exception e ) {
             throw new BuilderException( "Failed to build a concrete object", e );
           }
      }

      try {
         return this.invokeBuild( new Object[]{input} );
      }
      catch( InvocationTargetException e ) {
         Throwable cause = e.getCause();
         throw new BuilderException( cause.getMessage(), cause );
      }
      catch( Exception e ) {
         throw new BuilderException( "Failed to build a concrete object", e );
      }
   }

   /**
    * Constructs and returns an object by invoking a build method using a generic 
    * input object array.  This method prevents recursive calling of build method 
    * when a single Object is passed in for build method.
    *
    * @param inputs data from which an object is constructed
    * @throws InvocationTargetException Thrown if failed to invoke a method
    * @throws NoSuchMethodException Thrown if failed to find a method
    * @return Object a constructed object
    */
   public Object invokeBuild(Object[] inputs) throws InvocationTargetException, NoSuchMethodException, ReflectionException
   {
       Method method = Reflector.findMethod( this, "build", inputs );
       Class[] paramTypes = method.getParameterTypes();

       if((paramTypes != null) && (paramTypes.length == 1) && (Object.class.equals(paramTypes[0]))) {
          throw new NoSuchMethodException();
       }
       else {
          return Reflector.invoke( this, "build", inputs );
       }
   }
}