package test.encoding.soapenc;

import javax.xml.parsers.*;  // JAXP interfaces
import org.w3c.dom.*;
import org.apache.soap.util.Bean;
import org.apache.soap.util.xml.Deserializer;
import java.io.ByteArrayInputStream;

import junit.framework.TestCase;

/**
 * This floating point deserializer test class serves
 * as a superclass for 4 test classes - one each for
 * float, Float, double, and Double.  This allows these
 * tests to be re-used for each type of floating point
 * deserialization, with the only difference being
 * the particular type of floating point object to test.
 *
 * @author Jim Stearns (Jim_Stearns@hp.com)
 */
public class FPDeserializerTest extends TestCase
{
    // For the assert comparison of two floating point values.
    private static final double okDblDelta = 0.000001d;
    private static final float okFltDelta = 0.000001f;

    private Deserializer fpObjectToTest;

    public FPDeserializerTest(String name)
    {
        super(name);
    }

    /* This constructor allows this same class to test all of the
     * floating types: float, Float, double, and Double.
     */
    public FPDeserializerTest(String name, String fpClassToTest)
        throws ClassNotFoundException, IllegalAccessException,
            InstantiationException
    {
        super(name);
        Class testClass = Class.forName(fpClassToTest);
        fpObjectToTest = (Deserializer) testClass.newInstance();
    }

    private Node domFromXMLString(String str) throws Exception
    {
        DocumentBuilderFactory  factory = 
            DocumentBuilderFactory.newInstance();
        DocumentBuilder         builder;
        Document                document;
        builder = factory.newDocumentBuilder();

        ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes());
        document = builder.parse(bais);
        return document.getDocumentElement();
    }

    private Bean runUnmarshall(String fpVal) throws Exception
    {
        Node myNode = domFromXMLString(
            "<fpValue>" + fpVal + "</fpValue>");
        return fpObjectToTest.unmarshall("dontCare", /* inScopeEncStyle */
                null,       /* QName: dontcare */
                myNode,
                null,       /* XMLJavaMappingRegistry: dontcare */
                null);      /* SOAPContext: dontcare */
    }

    /* Convention: regardless of type of Bean (float, Double, ...),
     * the good value is always passed in as a Double.
     */
    private void assertBeanValueEquals(Bean bean, Double goodVal)
        throws Exception
    {
        // Workaround for JUnit defect: will submit to Kent Beck
        if (bean.value.toString().equals(goodVal.toString()))
            return;       

        if (bean.type == Float.class) {
            Float result = (Float) bean.value;
            float fpGoodVal = goodVal.floatValue();
            assertEquals(fpGoodVal, result.floatValue(), okFltDelta);
        } else if (bean.type == float.class) {
            float result = ((Float)bean.value).floatValue();
            float fpGoodVal = goodVal.floatValue();
            assertEquals(fpGoodVal, result, okFltDelta);
        } else if (bean.type == Double.class) {
            Double result = (Double) bean.value;
            double fpGoodVal = goodVal.doubleValue();
            assertEquals(fpGoodVal, result.doubleValue(), okDblDelta);
        } else if (bean.type == double.class) {
            double result = ((Double)bean.value).doubleValue();
            double fpGoodVal = goodVal.doubleValue();
            assertEquals(fpGoodVal, result, okDblDelta);
        } else {
            throw new Exception("Unexpected Bean type");
        }
    }

    public void testGoodValue1() throws Exception
    {
        Bean bean = runUnmarshall("1.1");
        assertBeanValueEquals(bean, new Double("1.1"));
    }

    public void testGoodValue2() throws Exception
    {
        Bean bean = runUnmarshall("0");
        assertBeanValueEquals(bean, new Double("0"));
    }

    public void testGoodValue3() throws Exception
    {
        Bean bean = runUnmarshall("-1E4");
        assertBeanValueEquals(bean, new Double("-1E4"));
    }

    public void testGoodValue4() throws Exception
    {
        Bean bean = runUnmarshall("0");
        assertBeanValueEquals(bean, new Double("0"));
    }

    public void testGoodValue5() throws Exception
    {
        Bean bean = runUnmarshall("-0");
        assertBeanValueEquals(bean, new Double("0"));
    }

    public void testGoodValue6() throws Exception
    {
        Bean bean = runUnmarshall("12.78e-12");
        assertBeanValueEquals(bean, new Double("12.78e-12"));
    }

    public void testBadValue() throws Exception
    {
        try {
            runUnmarshall("NoSuchSpecialFPValue");
            fail("Didn't get expected NumberFormatException");
        } catch (NumberFormatException nfe) {
            return;
        }
    }

    public void testBadCloseValue() throws Exception
    {
        try {
            runUnmarshall("InfinityAndBeyond");
            fail("Didn't get expected NumberFormatException");
        } catch (NumberFormatException nfe) {
            return;
        }
    }

    public void testGoodInf() throws Exception
    {
        Bean bean = runUnmarshall("INF");
        assertBeanValueEquals(bean, new Double(Double.POSITIVE_INFINITY));
    }

    /*
     * A forgiving listener: allow what Java Float.toString()
     * generates for Float.POSITIVE_INFINITY.
     */
    public void testGoodEnoughInfinity() throws Exception
    {
        Bean bean = runUnmarshall("Infinity");
        assertBeanValueEquals(bean, new Double(Double.POSITIVE_INFINITY));
    }

    public void testGoodEnoughInfinityCaseInsensitive() throws Exception
    {
        Bean bean = runUnmarshall("InFiNiTy");
        assertBeanValueEquals(bean, new Double(Double.POSITIVE_INFINITY));
    }

    public void testGoodNegInf() throws Exception
    {
        Bean bean = runUnmarshall("-INF");
        assertBeanValueEquals(bean, new Double(Double.NEGATIVE_INFINITY));
    }

    /*
     * A forgiving listener: allow what Java Float.toString()
     * generates for Float.NEGATIVE_INFINITY.
     */
    public void testGoodEnoughNegativeInfinity() throws Exception
    {
        Bean bean = runUnmarshall("-Infinity");
        assertBeanValueEquals(bean, new Double(Double.NEGATIVE_INFINITY));
    }

    public void testGoodNaN() throws Exception
    {
        Bean bean = runUnmarshall("NaN");
        assertBeanValueEquals(bean, new Double(Double.NaN));
    }

}
