package listFW.test;

import junit.framework.TestCase;
import listFW.*;
import listFW.factory.*;
import listFW.visitor.*;

/**
 * A JUnit test case class.
 */
public class Test_NewList extends TestCase {
    
    /**
     * Test the internal toString() method of a list
     */
    public void test_toString() {
        IListFactory<String> f = new CompositeListFactory<String>();
        
        IList<String> l = f.makeEmptyList();
        assertEquals("Empty list", "()", l.toString());
        l = f.makeNEList("a",l);
        //System.out.println("class = "+l.getClass());
        assertEquals("(a)", "(a)", l.toString());
        l = f.makeNEList("b",l);
        assertEquals("(b, a)", "(b, a)", l.toString());
        l = f.makeNEList("c",l);
        assertEquals("(c, b, a)", "(c, b, a)", l.toString());
    }
    
    /**
     * Custom class used by the test_ToStringAlgo test
     */
    class TestClass {
        private String s;
        public TestClass(String s) {
            this.s = s;
        }
        
        public String toString() {
            return "["+s+"]";
        }
    }
    
    /**
     * Tests the universality of the ToStringAlgo visitor.
     */
    public void test_ToStringAlgo() {
        IListFactory<String> fstr = new CompositeListFactory<String>();
        
        IListAlgo<Object, ?, ?> algo = ToStringAlgo.Singleton;
        
        IList<String> lstr = fstr.makeEmptyList();
        assertEquals("Empty list", "()", lstr.execute(algo));
        lstr = fstr.makeNEList("a",lstr);
        assertEquals("(a)", "(a)", lstr.execute(algo));
        lstr = fstr.makeNEList("b",lstr);
        assertEquals("(b, a)", "(b, a)", lstr.execute(algo));
        lstr = fstr.makeNEList("c",lstr);
        assertEquals("(c, b, a)", "(c, b, a)", lstr.execute(algo));
        
        IListFactory<Double> fdoub = new CompositeListFactory<Double>();
        IList<Double> ldoub = fdoub.makeEmptyList();
        assertEquals("Empty list", "()", ldoub.execute(algo));
        ldoub = fdoub.makeNEList(1.2,ldoub);
        assertEquals("(1.2)", "(1.2)", ldoub.execute(algo));
        ldoub = fdoub.makeNEList(3.4,ldoub);
        assertEquals("(3.4, 1.2)", "(3.4, 1.2)", ldoub.execute(algo));
        ldoub = fdoub.makeNEList(5.678,ldoub);
        assertEquals("(5.678, 3.4, 1.2)", "(5.678, 3.4, 1.2)", ldoub.execute(algo));
        
        IListFactory<TestClass> ftc = new CompositeListFactory<TestClass>();
        IList<TestClass> ltc = ftc.makeEmptyList();
        assertEquals("Empty list", "()", ltc.execute(algo));
        ltc = ftc.makeNEList(new TestClass("yahoo"),ltc);
        assertEquals("([yahoo])", "([yahoo])", ltc.execute(algo));
        ltc = ftc.makeNEList(new TestClass("google"),ltc);
        assertEquals("([google], [yahoo])", "([google], [yahoo])", ltc.execute(algo));
        ltc = ftc.makeNEList(new TestClass("msn"),ltc);
        assertEquals("([msn], [google], [yahoo])", "([msn], [google], [yahoo])", ltc.execute(algo));
    }
    
    /**
     * Test a more complex visitor that uses an input parameter that is 
     * not itself parameterized.
     */
    public void test_MakePhrase() {
        IListFactory<String> f = new CompositeListFactory<String>();
        IListAlgo<String, String, String> algo = MakePhrase.Singleton;
        
        IList<String> l = f.makeEmptyList();
        assertEquals("Empty list", "", l.execute(algo));
        l = f.makeNEList("a", l);
        assertEquals("(a)","[ a ]", l.execute(algo, "[","]"));
        l = f.makeNEList("b",l);
        assertEquals("(b, a)", "[ b ][ a ]", l.execute(algo, "[","]"));
        l = f.makeNEList("c",l);
        assertEquals("(c, b, a)", "[ c ][ b ][ a ]", l.execute(algo, "[","]"));
        
        // Note the semantic difficulties that arise from the fact that the following test passes
        // The visitor is called with only one parameter, not two, but it behaves as if two were passed.
        // This is language issue, not a visitor issue.
        assertEquals("(c, b, a)", "[ c ][ b ][ a ]", l.execute(algo, new String[]{"[","]"}));
        
    }
    
    /**
     * Test the parameterized list factory
     */
    public void test_factory() {
        IListFactory<Integer> fint = new CompositeListFactory<Integer>();
        
        IList<Integer> lint = fint.makeEmptyList();
        assertEquals("Empty Integer list", "()", lint.toString());
        
        lint = fint.makeNEList(5, lint);
        //fint.makeNEList(5.2, lint);  // should not compile
        assertEquals("(5)", "(5)", lint.toString());
        
        IListFactory<Double> fdoub = new CompositeListFactory<Double>();
        IList<Double> ldoub = fdoub.makeEmptyList();
        assertEquals("Empty Double list", "()", ldoub.toString());
        
        // heterogeneous lists.   The following lines should not compile
        // ldoub = fdoub.makeNEList(5.2, lint);
        //IList<Integer> l = fint.makeNEList(7, ldoub);
    }
    
    /**
     * Test the summing of a list of integers
     */
    public void test_SumIntList() {
        IListAlgo<Integer, ?, ?> algo = new SumIntList();
        
        IListFactory<Integer> fint = new CompositeListFactory<Integer>();
        
        IList<Integer> lint = fint.makeEmptyList(); 
        assertEquals("Empty list", 0, lint.execute(algo));
        
        lint = fint.makeNEList(31, lint);
        assertEquals("(31)", 31, lint.execute(algo));
        
        lint = fint.makeNEList(7, lint);
        assertEquals("(7, 31)", 38, lint.execute(algo));
        
        IListFactory<Double> fdoub = new CompositeListFactory<Double>();
        
        // The following line won't even compile any more because heterogeneous lists 
        // are disallowed.
        //    lint = fdoub.makeNEList(1.2, lint);
        // 
        //    // algo for a homogeneous list should fail on a heterogeneous list
        //    try {
        //      fail("heterogeneous list "+lint+": result = " +lint.execute(algo));
        //    } 
        //    catch(ClassCastException e) {
        //      // this exception should be thrown
        //    }
        //    catch(Exception e) {
        //      fail("heterogeneous list "+lint+": Exception = "+ e);
        //    }
        
        
    }
    
    /**
     * Test the summing of a list of numbers, which should work on a list of 
     * integers or a list of doubles.
     */
    public void test_SumNumList() {
        
        IListAlgo<Number, ?, ?> algo = new SumNumList();
        
        //Try on list of Integers
        IListFactory<Integer> fint = new CompositeListFactory<Integer>();
        
        IList<Integer> lint = fint.makeEmptyList(); 
        assertEquals("Empty list", 0, lint.execute(algo));
        
        lint = fint.makeNEList(31, lint);
        assertEquals("(31)", 31.0, lint.execute(algo));
        
        lint = fint.makeNEList(7, lint);
        assertEquals("(7, 31)", 38.0, lint.execute(algo));
        
        //Try same algo on list of Doubles
        IListFactory<Double> fdoub = new CompositeListFactory<Double>();
        
        IList<Double> ldoub = fdoub.makeEmptyList(); 
        assertEquals("Empty list", 0, ldoub.execute(algo));
        
        ldoub = fdoub.makeNEList(31.2, ldoub);
        assertEquals("(31.2)", 31.2, ldoub.execute(algo));
        
        ldoub = fdoub.makeNEList(7.5, ldoub);
        assertEquals("(7.5, 31.2)", 38.7, ldoub.execute(algo));
    }
    
    /**
     * Test the summing of the integer part of a list of numbers.  This is just
     * to show that an Integer can be returned when a Number is specified.
     */
    public void test_SumIntValList() {
        
        IListAlgo<Number, ?, ?> algo = new SumIntValList();
        
        // Note that the algo returns an Integer, not a Number, and that assertEquals
        // is sensitive to this.
        
        //Try on list of Integers
        IListFactory<Integer> fint = new CompositeListFactory<Integer>();
        
        IList<Integer> lint = fint.makeEmptyList(); 
        assertEquals("Empty list", 0, lint.execute(algo));
        
        lint = fint.makeNEList(31, lint);
        assertEquals("(31)", 31, lint.execute(algo));
        
        lint = fint.makeNEList(7, lint);
        assertEquals("(7, 31)", 38, lint.execute(algo));
        
        //Try same algo on list of Doubles
        IListFactory<Double> fdoub = new CompositeListFactory<Double>();
        
        IList<Double> ldoub = fdoub.makeEmptyList(); 
        assertEquals("Empty list", 0, ldoub.execute(algo));
        
        ldoub = fdoub.makeNEList(31.2, ldoub);
        assertEquals("(31.2)", 31, ldoub.execute(algo));
        
        ldoub = fdoub.makeNEList(7.5, ldoub);
        assertEquals("(7.5, 31.2)", 38, ldoub.execute(algo));
    }
    
    
    /**
     * Test a forward accumulation algorithm which is an algorithm that takes 
     * an unparameterized input value.
     */
    public void test_SumIntList_Fwd() {
        IListAlgo<Integer, ?, ?> algo = new SumIntList_Fwd();
        
        IListFactory<Integer> fint = new CompositeListFactory<Integer>();
        
        IList<Integer> lint = fint.makeEmptyList(); 
        assertEquals("Empty list", 0, lint.execute(algo));
        
        lint = fint.makeNEList(31, lint);
        assertEquals("(31)", 31, lint.execute(algo));
        
        lint = fint.makeNEList(7, lint);
        assertEquals("(7, 31)", 38, lint.execute(algo));
    }  
    
    
    /**
     * Test copying the list using a copy visitor that takes the factory as its 
     * input parameter.
     * Notice how the visitor (algo) is specfied as taking an IListFactory<?>
     * which is not typed to be specifically the same as the list's type.
     */
    public void test_CopyList() {
        IListAlgo<Integer, IList<Integer>, IListFactory<?>> algo = new CopyList<Integer>();
        
        IListFactory<Integer> fint = new CompositeListFactory<Integer>();
        
        IList<Integer> lint = fint.makeEmptyList(); 
        assertEquals("Empty list", "()", lint.execute(algo, fint).toString());
        
        lint = fint.makeNEList(31, lint);
        assertEquals("(31)", "(31)", lint.execute(algo, fint).toString());
        
        lint = fint.makeNEList(7, lint);
        assertEquals("(7, 31)", "(7, 31)", lint.execute(algo, fint).toString());
    }  
    
    /**
     * Test copying the list using a copy visitor that takes the factory in its 
     * constructor and thus does not use its parameter.
     */
    public void test_CopyList2() {
        IListFactory<Integer> fint = new CompositeListFactory<Integer>();
        IListAlgo<Integer, IList<Integer>, Object> algo = new CopyList2<Integer>(fint);
        
        IList<Integer> lint = fint.makeEmptyList(); 
        assertEquals("Empty list", "()", lint.execute(algo).toString());
        
        lint = fint.makeNEList(31, lint);
        assertEquals("(31)", "(31)", lint.execute(algo).toString());
        
        lint = fint.makeNEList(7, lint);
        assertEquals("(7, 31)", "(7, 31)", lint.execute(algo).toString());
    }  
    
    /**
     * Test copying the list using a copy visitor that takes the factory as a parameter
     */
    public void test_CopyList3() {
        IListFactory<Integer> fint = new CompositeListFactory<Integer>();
        IListAlgo<Integer, IList<Integer>, IListFactory<Integer>> algo = new CopyList3<Integer>();
        
        IList<Integer> lint = fint.makeEmptyList(); 
        assertEquals("Empty list", "()", lint.execute(algo,fint).toString());
        
        lint = fint.makeNEList(31, lint);
        assertEquals("(31)", "(31)", lint.execute(algo, fint).toString());
        
        lint = fint.makeNEList(7, lint);
        assertEquals("(7, 31)", "(7, 31)", lint.execute(algo, fint).toString());
    }  
    
    public void test_ReverseList() {
        IListAlgo<Integer, IList<Integer>, IListFactory<Integer>> algo = new ReverseList<Integer>();
        
        IListFactory<Integer> fint = new CompositeListFactory<Integer>();
        
        IList<Integer> lint = fint.makeEmptyList(); 
        assertEquals("Empty list", "()", lint.execute(algo, fint).toString());
        
        lint = fint.makeNEList(31, lint);
        assertEquals("(31)", "(31)", lint.execute(algo, fint).toString());
        
        lint = fint.makeNEList(7, lint);
        assertEquals("(7, 31)", "(31, 7)", lint.execute(algo, fint).toString());
        
        lint = fint.makeNEList(5, lint);
        assertEquals("(5, 7, 31)", "(31, 7, 5)", lint.execute(algo, fint).toString());
    }  
    
    public void test_ReverseList2() {
        IListAlgo<Integer, IList<Integer>, IListFactory<Integer>> algo = new ReverseList2<Integer>();
        
        IListFactory<Integer> fint = new CompositeListFactory<Integer>();
        
        IList<Integer> lint = fint.makeEmptyList(); 
        IList<Integer> result = lint.execute(algo, fint);
        
        assertEquals("Empty list", "()", lint.execute(algo, fint).toString());
        
        lint = fint.makeNEList(31, lint);
        assertEquals("(31)", "(31)", lint.execute(algo, fint).toString());
        
        lint = fint.makeNEList(7, lint);
        assertEquals("(7, 31)", "(31, 7)", lint.execute(algo, fint).toString());
        
        lint = fint.makeNEList(5, lint);
        assertEquals("(5, 7, 31)", "(31, 7, 5)", lint.execute(algo, fint).toString());
    }  
    
    
    public void test_ReverseList3() {
        IListAlgo<Object, IList<? extends Object>, IListFactory<?>> algo = new ReverseList3();
        
        IListFactory<Integer> fint = new CompositeListFactory<Integer>();
        
        IList<Integer> lint = fint.makeEmptyList(); 
        //Note that the following cast is required b/c the return type os Object, not a list.
        IList<Integer> result = (IList<Integer>) lint.execute(algo, fint);
        
        assertEquals("Empty list", "()", result.toString());
        
        
        lint = fint.makeNEList(31, lint);
        assertEquals("(31)", "(31)", lint.execute(algo, fint).toString());
        
        lint = fint.makeNEList(7, lint);
        assertEquals("(7, 31)", "(31, 7)", lint.execute(algo, fint).toString());
        
        lint = fint.makeNEList(5, lint);
        assertEquals("(5, 7, 31)", "(31, 7, 5)", lint.execute(algo, fint).toString());
    }  
    
}
