// Copyright 2008-2012 severally by the contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package net.sf.practicalxml.converter.internal;

import java.io.File;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;
import java.util.TimeZone;

import net.sf.practicalxml.converter.ConversionException;
import net.sf.practicalxml.converter.bean.AbstractBeanConverterTestCase;
import net.sf.practicalxml.converter.internal.JavaStringConversions;


public class TestJavaStringConversions
extends AbstractBeanConverterTestCase
{
    private JavaStringConversions _converter = new JavaStringConversions(false);

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


//----------------------------------------------------------------------------
//  Support Code
//----------------------------------------------------------------------------

    private void assertFailsConversionToObject(
            String message, String str, Class<?> klass)
    {
        try
        {
            _converter.parse(str, klass);
            fail(message);
        }
        catch (ConversionException ee)
        {
            // success!
        }
    }


//----------------------------------------------------------------------------
//  Test Cases
//----------------------------------------------------------------------------


    public void testConvertNull() throws Exception
    {
        assertNull(_converter.stringify(null));
        assertNull(_converter.parse(null, Object.class));
    }


    public void testConvertString() throws Exception
    {
        assertEquals("foo", _converter.stringify("foo"));
        assertEquals("foo", _converter.parse("foo", String.class));

        assertEquals("", _converter.stringify(""));
        assertEquals("", _converter.parse("", String.class));
    }


    public void testConvertCharacter() throws Exception
    {
        Character simple = Character.valueOf('A');
        assertEquals("A", _converter.stringify(simple));
        assertEquals(simple, _converter.parse("A", Character.class));

        Character nul = Character.valueOf('\0');
        assertEquals("", _converter.stringify(nul));
        assertEquals(nul, _converter.parse("", Character.class));

        assertFailsConversionToObject(
                "converted multi-character string",
                "ix", Character.class);
    }


    public void testConvertBooleanDefault() throws Exception
    {
        String sTrue = Boolean.TRUE.toString();
        assertEquals(sTrue, _converter.stringify(Boolean.TRUE));
        assertEquals(Boolean.TRUE, _converter.parse(sTrue, Boolean.class));

        String sFalse = Boolean.FALSE.toString();
        assertEquals(sFalse, _converter.stringify(Boolean.FALSE));
        assertEquals(Boolean.FALSE, _converter.parse(sFalse, Boolean.class));

        assertEquals(Boolean.FALSE, _converter.parse("ix", Boolean.class));
        assertEquals(Boolean.FALSE, _converter.parse("", Boolean.class));
    }


    public void testConvertBooleanXsd() throws Exception
    {
        _converter = new JavaStringConversions(true);

        assertEquals("true", _converter.stringify(Boolean.TRUE));
        assertEquals(Boolean.TRUE, _converter.parse("true", Boolean.class));
        assertEquals(Boolean.TRUE, _converter.parse("1", Boolean.class));

        assertEquals("false", _converter.stringify(Boolean.FALSE));
        assertEquals(Boolean.FALSE, _converter.parse("false", Boolean.class));
        assertEquals(Boolean.FALSE, _converter.parse("0", Boolean.class));

        assertFailsConversionToObject(
                "converted multi-character string",
                "ix", Boolean.class);

        assertFailsConversionToObject(
                "converted empty string",
                "", Boolean.class);
    }


    public void testConvertByte() throws Exception
    {
        String str1 = "123";
        Byte val1 = Byte.valueOf((byte)123);
        assertEquals(str1, _converter.stringify(val1));
        assertEquals(val1, _converter.parse(str1, Byte.class));

        String str2 = "-123";
        Byte val2 = Byte.valueOf((byte)-123);
        assertEquals(str2, _converter.stringify(val2));
        assertEquals(val2, _converter.parse(str2, Byte.class));

        String str3 = "   -123   ";
        Byte val3 = Byte.valueOf((byte)-123);
        assertEquals(val3, _converter.parse(str3, Byte.class));

        assertFailsConversionToObject(
                "converted too-large value",
                "1234567", Byte.class);

        assertFailsConversionToObject(
                "converted non-integer value",
                "1.23", Byte.class);

        assertFailsConversionToObject(
                "converted non-numeric value",
                "ix", Byte.class);

        assertFailsConversionToObject(
                "converted empty string",
                "", Byte.class);
    }


    public void testConvertShort() throws Exception
    {
        String str1 = "12345";
        Short val1 = Short.valueOf((short)12345);
        assertEquals(str1, _converter.stringify(val1));
        assertEquals(val1, _converter.parse(str1, Short.class));

        String str2 = "-12345";
        Short val2 = Short.valueOf((short)-12345);
        assertEquals(str2, _converter.stringify(val2));
        assertEquals(val2, _converter.parse(str2, Short.class));

        String str3 = "   -12345   ";
        Short val3 = Short.valueOf((short)-12345);
        assertEquals(val3, _converter.parse(str3, Short.class));

        assertFailsConversionToObject(
                "converted too-large value",
                "1234567", Short.class);

        assertFailsConversionToObject(
                "converted non-integer value",
                "123.45", Short.class);

        assertFailsConversionToObject(
                "converted non-numeric value",
                "ix", Short.class);

        assertFailsConversionToObject(
                "converted empty string",
                "", Short.class);
    }


    public void testConvertInteger() throws Exception
    {
        String str1 = "1234567";
        Integer val1 = Integer.valueOf(1234567);
        assertEquals(str1, _converter.stringify(val1));
        assertEquals(val1, _converter.parse(str1, Integer.class));

        String str2 = "-1234567";
        Integer val2 = Integer.valueOf(-1234567);
        assertEquals(str2, _converter.stringify(val2));
        assertEquals(val2, _converter.parse(str2, Integer.class));

        String str3 = "   -1234567   ";
        Integer val3 = Integer.valueOf(-1234567);
        assertEquals(val3, _converter.parse(str3, Integer.class));

        assertFailsConversionToObject(
                "converted too-large value",
                "123456789012345", Integer.class);

        assertFailsConversionToObject(
                "converted non-integer value",
                "123.45", Integer.class);

        assertFailsConversionToObject(
                "converted non-numeric value",
                "ix", Integer.class);

        assertFailsConversionToObject(
                "converted empty string",
                "", Integer.class);
    }


    public void testConvertLong() throws Exception
    {
        String str1 = "1234567890";
        Long val1 = Long.valueOf(1234567890L);
        assertEquals(str1, _converter.stringify(val1));
        assertEquals(val1, _converter.parse(str1, Long.class));

        String str2 = "-1234567890";
        Long val2 = Long.valueOf(-1234567890L);
        assertEquals(str2, _converter.stringify(val2));
        assertEquals(val2, _converter.parse(str2, Long.class));

        String str3 = "   -1234567890   ";
        Long val3 = Long.valueOf(-1234567890L);
        assertEquals(val3, _converter.parse(str3, Long.class));

        assertFailsConversionToObject(
                "converted too-large value",
                "123456789012345678901234567890", Long.class);

        assertFailsConversionToObject(
                "converted non-integer value",
                "123.45", Long.class);

        assertFailsConversionToObject(
                "converted non-numeric value",
                "ix", Long.class);

        assertFailsConversionToObject(
                "converted empty string",
                "", Long.class);
    }


    public void testConvertFloatDefault() throws Exception
    {
        // note: for default-format tests, strings are generated from values
        Float val1 = Float.valueOf(1234f);
        String str1 = val1.toString();
        assertEquals(str1, _converter.stringify(val1));
        assertEquals(val1, _converter.parse(str1, Float.class));

        Float val2 = Float.valueOf(-1234f);
        String str2 = val2.toString();
        assertEquals(str2, _converter.stringify(val2));
        assertEquals(val2, _converter.parse(str2, Float.class));

        String str3 = "   -1234.5   ";
        Float val3 = Float.valueOf(-1234.5f);
        assertEquals(val3, _converter.parse(str3, Float.class));

        assertFailsConversionToObject(
                "converted non-numeric value",
                "ix", Float.class);

        assertFailsConversionToObject(
                "converted empty string",
                "", Float.class);
    }


    public void testConvertFloatXsd() throws Exception
    {
        String str1 = "1234.0";
        Float val1 = Float.valueOf(1234f);
        assertEquals(str1, _converter.stringify(val1));
        assertEquals(val1, _converter.parse(str1, Float.class));

        String str2 = "-1234.0";
        Float val2 = Float.valueOf(-1234f);
        assertEquals(str2, _converter.stringify(val2));
        assertEquals(val2, _converter.parse(str2, Float.class));

        String str3 = "   -1234.5   ";
        Float val3 = Float.valueOf(-1234.5f);
        assertEquals(val3, _converter.parse(str3, Float.class));

        assertFailsConversionToObject(
                "converted non-numeric value",
                "ix", Float.class);

        assertFailsConversionToObject(
                "converted empty string",
                "", Float.class);
    }


    public void testConvertDoubleDefault() throws Exception
    {
        // note: for default-format tests, strings are generated from values
        Double val1 = Double.valueOf(1234567890.5);
        String str1 = val1.toString();
        assertEquals(str1, _converter.stringify(val1));
        assertEquals(val1, _converter.parse(str1, Double.class));

        Double val2 = Double.valueOf(-1234567890.1);
        String str2 = val2.toString();
        assertEquals(str2, _converter.stringify(val2));
        assertEquals(val2, _converter.parse(str2, Double.class));

        String str3 = "   -1234.5   ";
        Double val3 = Double.valueOf(-1234.5);
        assertEquals(val3, _converter.parse(str3, Double.class));

        assertFailsConversionToObject(
                "converted non-numeric value",
                "ix", Double.class);

        assertFailsConversionToObject(
                "converted empty string",
                "", Double.class);
    }


    public void testConvertDoubleXsd() throws Exception
    {
        _converter = new JavaStringConversions(true);

        // while for XSD-format tests, we want to verify the strings
        String str1 = "1234567890.5";
        Double val1 = Double.valueOf(1234567890.5);
        assertEquals(str1, _converter.stringify(val1));
        assertEquals(val1, _converter.parse(str1, Double.class));

        String str2 = "-1234567890.1";
        Double val2 = Double.valueOf(-1234567890.1);
        assertEquals(str2, _converter.stringify(val2));
        assertEquals(val2, _converter.parse(str2, Double.class));

        String str3 = "   -1234.5   ";
        Double val3 = Double.valueOf(-1234.5);
        assertEquals(val3, _converter.parse(str3, Double.class));

        assertFailsConversionToObject(
                "converted non-numeric value",
                "ix", Double.class);

        assertFailsConversionToObject(
                "converted empty string",
                "", Double.class);
    }


    public void testConvertBigInteger() throws Exception
    {
        String str1 = "123456789012345678901234567890";
        BigInteger val1 = new BigInteger(str1);
        assertEquals(str1, _converter.stringify(val1));
        assertEquals(val1, _converter.parse(str1, BigInteger.class));

        String str2 = "-123456789012345678901234567890";
        BigInteger val2 = new BigInteger(str2);
        assertEquals(str2, _converter.stringify(val2));
        assertEquals(val2, _converter.parse(str2, BigInteger.class));

        assertFailsConversionToObject(
                "converted non-integer value",
                "123.45", BigInteger.class);

        assertFailsConversionToObject(
                "converted non-numeric value",
                "ix", BigInteger.class);

        assertFailsConversionToObject(
                "converted empty string",
                "", BigInteger.class);
    }


    public void testConvertBigDecimal() throws Exception
    {
        String str1 = "12345678901234567890.123456789";
        BigDecimal val1 = new BigDecimal(str1);
        assertEquals(str1, _converter.stringify(val1));
        assertEquals(val1, _converter.parse(str1, BigDecimal.class));

        String str2 = "-12345678901234567890.123456789";
        BigDecimal val2 = new BigDecimal(str2);
        assertEquals(str2, _converter.stringify(val2));
        assertEquals(val2, _converter.parse(str2, BigDecimal.class));

        assertFailsConversionToObject(
                "converted non-numeric value",
                "ix", BigDecimal.class);

        assertFailsConversionToObject(
                "converted empty string",
                "", BigDecimal.class);
    }


    @SuppressWarnings("rawtypes")
    public void testConvertClass() throws Exception
    {
        Class initial = String.class;

        String strValue = _converter.stringify(initial);
        assertEquals("java.lang.String", strValue);

        Object result = _converter.parse(strValue, Class.class);
        assertEquals(initial, result);
    }


    public void testConvertFile() throws Exception
    {
        File initial = File.createTempFile("TestConversionUtils", ".tmp");
        initial.deleteOnExit();

        String strValue = _converter.stringify(initial);
        assertEquals(initial.getCanonicalPath(), strValue);

        File result = (File)_converter.parse(strValue, File.class);
        assertEquals(initial.getCanonicalPath(), result.getCanonicalPath());

        initial.delete();
    }


    public void testConvertTimeZone() throws Exception
    {
        TimeZone initial = TimeZone.getDefault();

        String strValue = _converter.stringify(initial);
        assertEquals(TimeZone.getDefault().getID(), strValue);

        Object result = _converter.parse(strValue, TimeZone.class);
        assertEquals(initial, result);
    }


    public void testConvertCustomTimeZone() throws Exception
    {
        String id = "GMT+05:30";    // normalized form
        TimeZone initial = TimeZone.getTimeZone(id);

        String strValue = _converter.stringify(initial);
        assertEquals(id, strValue);

        Object result = _converter.parse(strValue, TimeZone.class);
        assertEquals(initial, result);
    }


    public void testConvertLocale() throws Exception
    {
        Locale initial = Locale.FRANCE;

        String strValue = _converter.stringify(initial);
        assertEquals("fr_FR", strValue);    // whitebox test

        Object result = _converter.parse(strValue, Locale.class);
        assertEquals(initial, result);
    }


    public void testConvertPartialLocales() throws Exception
    {
        Locale loc1 = (Locale)_converter.parse("en", Locale.class);
        assertEquals("en", loc1.getLanguage());
        assertEquals("", loc1.getCountry());
        assertEquals("", loc1.getVariant());

        Locale loc2 = (Locale)_converter.parse("_US", Locale.class);
        assertEquals("", loc2.getLanguage());
        assertEquals("US", loc2.getCountry());
        assertEquals("", loc2.getVariant());
    }
}
