ParserBaseNumberConversionTest.java

package tools.jackson.core.unittest.base;

import java.math.BigDecimal;
import java.math.BigInteger;

import org.junit.jupiter.api.Test;

import tools.jackson.core.JsonParser;
import tools.jackson.core.JsonToken;
import tools.jackson.core.unittest.JacksonCoreTestBase;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Tests for {@link tools.jackson.core.base.ParserBase} number conversion methods,
 * particularly testing the internal number representation conversion logic.
 */
public class ParserBaseNumberConversionTest extends JacksonCoreTestBase
{
    /**
     * Test conversion from int to other number types
     */
    @Test
    void intToOtherTypes() throws Exception
    {
        _testIntToOtherTypes(MODE_READER);
        _testIntToOtherTypes(MODE_INPUT_STREAM);
    }

    private void _testIntToOtherTypes(int mode) throws Exception
    {
        String json = "{\"value\": 42}";
        try (JsonParser p = createParser(mode, json)) {
            assertToken(JsonToken.START_OBJECT, p.nextToken());
            assertToken(JsonToken.PROPERTY_NAME, p.nextToken());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());

            // Access as int first
            assertEquals(42, p.getIntValue());

            // Then convert to long
            assertEquals(42L, p.getLongValue());

            // Convert to BigInteger
            assertEquals(BigInteger.valueOf(42), p.getBigIntegerValue());

            // Convert to float
            assertEquals(42.0f, p.getFloatValue());

            // Convert to double
            assertEquals(42.0, p.getDoubleValue());

            // Convert to BigDecimal
            assertEquals(new BigDecimal("42"), p.getDecimalValue());

            assertToken(JsonToken.END_OBJECT, p.nextToken());
        }
    }

    /**
     * Test conversion from long to other number types
     */
    @Test
    void longToOtherTypes() throws Exception
    {
        _testLongToOtherTypes(MODE_READER);
        _testLongToOtherTypes(MODE_INPUT_STREAM);
    }

    private void _testLongToOtherTypes(int mode) throws Exception
    {
        // Use a number larger than int max
        String json = "{\"value\": 9876543210}";
        try (JsonParser p = createParser(mode, json)) {
            assertToken(JsonToken.START_OBJECT, p.nextToken());
            assertToken(JsonToken.PROPERTY_NAME, p.nextToken());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());

            // Access as long first
            assertEquals(9876543210L, p.getLongValue());

            // Convert to BigInteger
            assertEquals(BigInteger.valueOf(9876543210L), p.getBigIntegerValue());

            // Convert to double
            assertEquals(9876543210.0, p.getDoubleValue());

            // Convert to BigDecimal
            assertEquals(new BigDecimal("9876543210"), p.getDecimalValue());

            assertToken(JsonToken.END_OBJECT, p.nextToken());
        }
    }

    /**
     * Test conversion from double to other number types
     */
    @Test
    void doubleToOtherTypes() throws Exception
    {
        _testDoubleToOtherTypes(MODE_READER);
        _testDoubleToOtherTypes(MODE_INPUT_STREAM);
    }

    private void _testDoubleToOtherTypes(int mode) throws Exception
    {
        String json = "{\"value\": 123.456}";
        try (JsonParser p = createParser(mode, json)) {
            assertToken(JsonToken.START_OBJECT, p.nextToken());
            assertToken(JsonToken.PROPERTY_NAME, p.nextToken());
            assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());

            // Access as double first
            assertEquals(123.456, p.getDoubleValue(), 0.001);

            // Convert to float
            assertEquals(123.456f, p.getFloatValue(), 0.001);

            // Convert to BigDecimal
            BigDecimal bd = p.getDecimalValue();
            assertTrue(bd.doubleValue() > 123.4 && bd.doubleValue() < 123.5);

            assertToken(JsonToken.END_OBJECT, p.nextToken());
        }
    }

    /**
     * Test conversion from BigInteger to other number types
     */
    @Test
    void bigIntegerToOtherTypes() throws Exception
    {
        _testBigIntegerToOtherTypes(MODE_READER);
        _testBigIntegerToOtherTypes(MODE_INPUT_STREAM);
    }

    private void _testBigIntegerToOtherTypes(int mode) throws Exception
    {
        // Use a very large number that exceeds long
        String json = "{\"value\": 12345678901234567890123456789}";
        try (JsonParser p = createParser(mode, json)) {
            assertToken(JsonToken.START_OBJECT, p.nextToken());
            assertToken(JsonToken.PROPERTY_NAME, p.nextToken());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());

            // Access as BigInteger first
            BigInteger bigInt = p.getBigIntegerValue();
            assertEquals(new BigInteger("12345678901234567890123456789"), bigInt);

            // Convert to BigDecimal
            BigDecimal bd = p.getDecimalValue();
            assertEquals(new BigDecimal("12345678901234567890123456789"), bd);

            // Convert to double (will lose precision)
            double d = p.getDoubleValue();
            assertTrue(d > 1.0e28);

            assertToken(JsonToken.END_OBJECT, p.nextToken());
        }
    }

    /**
     * Test conversion from BigDecimal to other number types
     */
    @Test
    void bigDecimalToOtherTypes() throws Exception
    {
        _testBigDecimalToOtherTypes(MODE_READER);
        _testBigDecimalToOtherTypes(MODE_INPUT_STREAM);
    }

    private void _testBigDecimalToOtherTypes(int mode) throws Exception
    {
        // Use a precise decimal number
        String json = "{\"value\": 123.456789012345}";
        try (JsonParser p = createParser(mode, json)) {
            assertToken(JsonToken.START_OBJECT, p.nextToken());
            assertToken(JsonToken.PROPERTY_NAME, p.nextToken());
            assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());

            // Access as BigDecimal first
            BigDecimal bd = p.getDecimalValue();
            assertTrue(bd.toString().startsWith("123.456789"));

            // Convert to double
            assertEquals(123.456789012345, p.getDoubleValue(), 0.000001);

            // Convert to float (will lose precision)
            float f = p.getFloatValue();
            assertEquals(123.456789f, f, 0.01f);

            assertToken(JsonToken.END_OBJECT, p.nextToken());
        }
    }

    /**
     * Test number type detection for various integer sizes
     */
    @Test
    void numberTypeDetection() throws Exception
    {
        _testNumberTypeDetection(MODE_READER);
        _testNumberTypeDetection(MODE_INPUT_STREAM);
    }

    private void _testNumberTypeDetection(int mode) throws Exception
    {
        // Small int
        String json1 = "42";
        try (JsonParser p = createParser(mode, json1)) {
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.INT, p.getNumberType());
        }

        // Large long
        String json2 = "9876543210";
        try (JsonParser p = createParser(mode, json2)) {
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.LONG, p.getNumberType());
        }

        // Float
        String json3 = "123.456";
        try (JsonParser p = createParser(mode, json3)) {
            assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
            JsonParser.NumberType nt = p.getNumberType();
            assertTrue(nt == JsonParser.NumberType.DOUBLE || nt == JsonParser.NumberType.BIG_DECIMAL);
        }

        // Very large BigInteger
        String json4 = "12345678901234567890123456789";
        try (JsonParser p = createParser(mode, json4)) {
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(JsonParser.NumberType.BIG_INTEGER, p.getNumberType());
        }
    }

    /**
     * Test number value retrieval with different access patterns
     */
    @Test
    void numberValueAccessPatterns() throws Exception
    {
        _testNumberValueAccessPatterns(MODE_READER);
        _testNumberValueAccessPatterns(MODE_INPUT_STREAM);
    }

    private void _testNumberValueAccessPatterns(int mode) throws Exception
    {
        String json = "[42, 123.456, 9876543210, 12345678901234567890123456789]";
        try (JsonParser p = createParser(mode, json)) {
            assertToken(JsonToken.START_ARRAY, p.nextToken());

            // First: small int
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(42, p.getIntValue());
            assertEquals(42L, p.getLongValue());
            assertEquals(42.0, p.getDoubleValue());

            // Second: decimal
            assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
            assertEquals(123.456, p.getDoubleValue(), 0.001);
            assertEquals(123.456f, p.getFloatValue(), 0.001);

            // Third: long
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(9876543210L, p.getLongValue());

            // Fourth: BigInteger
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(new BigInteger("12345678901234567890123456789"),
                        p.getBigIntegerValue());

            assertToken(JsonToken.END_ARRAY, p.nextToken());
        }
    }

    /**
     * Test edge cases for number conversion
     */
    @Test
    void numberConversionEdgeCases() throws Exception
    {
        _testNumberConversionEdgeCases(MODE_READER);
        _testNumberConversionEdgeCases(MODE_INPUT_STREAM);
    }

    private void _testNumberConversionEdgeCases(int mode) throws Exception
    {
        // Zero
        String json = "{\"zero\": 0, \"negZero\": -0, \"one\": 1, \"negOne\": -1}";
        try (JsonParser p = createParser(mode, json)) {
            assertToken(JsonToken.START_OBJECT, p.nextToken());

            // zero
            assertToken(JsonToken.PROPERTY_NAME, p.nextToken());
            assertEquals("zero", p.currentName());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(0, p.getIntValue());
            assertEquals(0L, p.getLongValue());
            assertEquals(0.0, p.getDoubleValue());
            assertEquals(BigInteger.ZERO, p.getBigIntegerValue());

            // -0
            assertToken(JsonToken.PROPERTY_NAME, p.nextToken());
            assertEquals("negZero", p.currentName());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(0, p.getIntValue());

            // 1
            assertToken(JsonToken.PROPERTY_NAME, p.nextToken());
            assertEquals("one", p.currentName());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(1, p.getIntValue());

            // -1
            assertToken(JsonToken.PROPERTY_NAME, p.nextToken());
            assertEquals("negOne", p.currentName());
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(-1, p.getIntValue());

            assertToken(JsonToken.END_OBJECT, p.nextToken());
        }
    }

    /**
     * Test negative numbers conversion
     */
    @Test
    void negativeNumberConversions() throws Exception
    {
        _testNegativeNumberConversions(MODE_READER);
        _testNegativeNumberConversions(MODE_INPUT_STREAM);
    }

    private void _testNegativeNumberConversions(int mode) throws Exception
    {
        String json = "[-42, -123.456, -9876543210]";
        try (JsonParser p = createParser(mode, json)) {
            assertToken(JsonToken.START_ARRAY, p.nextToken());

            // Negative int
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(-42, p.getIntValue());
            assertEquals(-42L, p.getLongValue());
            assertEquals(-42.0, p.getDoubleValue());

            // Negative decimal
            assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
            assertEquals(-123.456, p.getDoubleValue(), 0.001);
            assertEquals(-123.456f, p.getFloatValue(), 0.001);

            // Negative long
            assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
            assertEquals(-9876543210L, p.getLongValue());
            assertEquals(-9876543210.0, p.getDoubleValue());

            assertToken(JsonToken.END_ARRAY, p.nextToken());
        }
    }
}