/* TP.java
 * Bryan Chadwick :: 2007
 * Constructing Function class for Traversals */

package edu.neu.ccs.demeterf;

import java.lang.reflect.*;
import edu.neu.ccs.demeterf.util.*;
import edu.neu.ccs.demeterf.dispatch.*;

/** Function class implementation that calls object constructors.  Used to rebuild
 *    a data structure while transforming. Programmers can extend this class
 *    to transform structures while traversing.
 */
public class TP extends ID{
    static boolean debug;
    public static void setDebug(boolean b){ debug = b; }
    public static boolean isDebug(){ return debug;}
    
    public Object combine(Object o){ return o; }
    
    public Object combine(Object o1, Object a) 
    { return help(o1.getClass(),o1, new Object[]{a}); }
    
    public Object combine(Object o1, Object o2, Object a)
    { return help(o1.getClass(),o1, new Object[]{o2,a}); }
    
    public Object combine(Object o1, Object o2, Object o3, Object a)
    { return help(o1.getClass(),o1, new Object[]{o2,o3,a}); }
    
    public Object combine(Object o1, Object o2, Object o3, Object o4, Object a)
    { return help(o1.getClass(),o1, new Object[]{o2,o3,o4,a}); }
    
    public Object combine(Object o1, Object o2, Object o3, Object o4, Object o5, Object a)
    { return help(o1.getClass(),o1, new Object[]{o2,o3,o4,o5,a}); }
    
    public Object combine(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object a)
    { return help(o1.getClass(),o1, new Object[]{o2,o3,o4,o5,o6,a}); }
    
    public Object combine(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7, Object a)
    { return help(o1.getClass(),o1, new Object[]{o2,o3,o4,o5,o6,o7,a}); }
    
    public Object combine(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7,
                          Object o8, Object a)
    { return help(o1.getClass(),o1, new Object[]{o2,o3,o4,o5,o6,o7,o8,a}); }
    
    public Object combine(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7,
            Object o8, Object o9, Object a)
    { return help(o1.getClass(),o1, new Object[]{o2,o3,o4,o5,o6,o7,o8,o9,a}); }
    
    public Object combine(Object o1, Object o2, Object o3, Object o4, Object o5, Object o6, Object o7,
            Object o8, Object o9, Object o10, Object a)
    { return help(o1.getClass(),o1, new Object[]{o2,o3,o4,o5,o6,o7,o8,o9,o10,a}); }

    private Object help(Class<?> c, Object def, Object o[]){
        int fields = Util.getFuncFields(c).length();
        if(fields == 0)return def;
        Class<?> cs[] = Util.classesFromObjects(o, Math.min(fields,o.length));
        MethodDB<Constructor<?>> mdb = MethodDB.createConstrDB(c);
        DBEntry<Constructor<?>> cone = mdb.matchEntryFast(cs);
        if(cone == null){
            System.err.println("\n"+mdb.toString("\n")+"\n");
            throw new BuilderException("\n ** TP error: Did not find Constructor \n    "+
                    Util.signature(c, "<init>", cs, cs.length)+"\n");
        }
        /*if(con.getParameterTypes().length < fields){
            System.out.println("BAD("+fields+"):"+c.getName());
        }*/
        try{
            Constructor<?> con = cone.getMethod();
            if(TP.isDebug()){
                System.out.println("  GOT: "+Util.signature(c, c.getSimpleName(), cs, cs.length));
                System.out.println("USING: "+Util.signature(con));
            }
            con.setAccessible(true);
            return con.newInstance(Util.objectSubset(o, con.getParameterTypes().length));
        }catch(java.lang.reflect.InvocationTargetException ite){
            throw (RuntimeException)ite.getCause();
        }catch(Exception e){
            e.printStackTrace();
            throw (RuntimeException)e;
        }
    }

}