AnnotationExpressionTest.java

package com.thoughtworks.qdox;

import com.thoughtworks.qdox.builder.impl.EvaluatingVisitor;
import com.thoughtworks.qdox.model.JavaAnnotation;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.expression.AnnotationValue;
import com.thoughtworks.qdox.parser.ParseException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.io.StringReader;
import java.util.Arrays;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

class AnnotationExpressionTest
{
    private JavaProjectBuilder builder;

    @BeforeEach
    public void setUp()
    {
        builder = new JavaProjectBuilder();
    }

    static Stream<Arguments> expressions()
    {
        return Stream.of(
            // testPrecedence
            Arguments.arguments( "2 + 2 * 5", new Integer( 12 ) ),
            Arguments.arguments( "2 * 5 + 2", new Integer( 12 ) ),
            Arguments.arguments( "2+2*5", new Integer( 12 ) ),
            Arguments.arguments( "2*5+2", new Integer( 12 ) ),

            // testLogicalExpression
            Arguments.arguments( "true && false", Boolean.FALSE ),
            Arguments.arguments( "true || false", Boolean.TRUE ),
            Arguments.arguments( "!true", Boolean.FALSE ),

            // testBitExpression
            Arguments.arguments( "1 & 3", new Integer( 1 & 3 ) ),
            Arguments.arguments( "1 | 3", new Integer( 1 | 3 ) ),
            Arguments.arguments( "1 ^ 3", new Integer( 1 ^ 3 ) ),
            Arguments.arguments( "~1", new Integer( ~1 ) ),

            // testSignExpression
            Arguments.arguments( "+1", new Integer( 1 ) ),
            Arguments.arguments( "-1", new Integer( -1 ) ),
            //{ "--1", new Integer( --1 ) ),

            // testAddSubMultDivExpression
            Arguments.arguments( "8 / 3", new Integer( 8 / 3 ) ),
            Arguments.arguments( "8 * 3", new Integer( 8 * 3 ) ),
            Arguments.arguments( "8 + 3", new Integer( 8 + 3 ) ),
            Arguments.arguments( "8 - 3", new Integer( 8 - 3 ) ),
            Arguments.arguments( "8 % 3", new Integer( 8 % 3 ) ),
            Arguments.arguments( "\"a\" + \"b\"", "a" + "b" ),

            // testShiftExpression
            Arguments.arguments( "8 << 2", new Integer( 8 << 2 ) ),
            Arguments.arguments( "8 >> 2", new Integer( 8 >> 2 ) ),
            Arguments.arguments( "-1 >> 2", new Integer( -1 >> 2 ) ),
            Arguments.arguments( "-1 >>> 2", new Integer( -1 >>> 2 ) ),

            // testLiteral
            Arguments.arguments( "1", new Integer( 1 ) ),
            Arguments.arguments( "1l", new Long( 1 ) ),
            Arguments.arguments( "1.0", new Float( 1 ) ),
            Arguments.arguments( "1.0d", new Double( 1 ) ),

            // testParenExpression
            Arguments.arguments( "2 + (2 * 5)", new Integer( 12 ) ),
            Arguments.arguments( "(2 + 2) * 5", new Integer( 20 ) ),

            // testCompareExpression
            Arguments.arguments( "1 < 2", Boolean.TRUE ),
            Arguments.arguments( "1 > 2", Boolean.FALSE ),
            Arguments.arguments( "1 <= 2", Boolean.TRUE ),
            Arguments.arguments( "1 >= 2", Boolean.FALSE ),
            Arguments.arguments( "1 == 2", Boolean.FALSE ),
            Arguments.arguments( "1 != 2", Boolean.TRUE ),

            // testQueryExpression
            Arguments.arguments( "1 < 2 ? 0 : 3", new Integer( 0 ) ),
            Arguments.arguments( "1 > 2 ? 0 : 3", new Integer( 3 ) ),

            // testCastExpression
            Arguments.arguments( "(short)1", new Short( (short) 1 ) ),
            Arguments.arguments( "(long)(short)1", new Long( 1 ) ),
            Arguments.arguments( "(int)((short)1 + (long)3)", new Integer( 4 ) ),

            // test Array-valued single-element annotation
            Arguments.arguments( "{\"Children\", \"Unscrupulous dentists\"}",
                Arrays.asList( new String[] { "Children", "Unscrupulous dentists" } ) ),

            // test Array-valued annotation ending with a comma
            Arguments.arguments( "{\"deprecated\", }", Arrays.asList( new String[] { "deprecated" } ) ),

            // Binary Literals (added to JDK7)
            Arguments.arguments( "(byte)0b00100001", Byte.parseByte( "100001", 2 ) ),
            Arguments.arguments( "(short)0b0110000101000101", Short.parseShort( "0110000101000101", 2 ) ),
            Arguments.arguments( "0b101", Integer.parseInt( "101", 2 ) ),
            Arguments.arguments( "0B101", Integer.parseInt( "101", 2 ) ),
            Arguments.arguments( "0b00010000101000101101000010100010110100001010001011010000101000101L",
                Long.parseLong( "00010000101000101101000010100010110100001010001011010000101000101", 2 ) ),

            // Underscores in Numeric Literals (added to JDK7)
            Arguments.arguments( "1234_5678", Integer.parseInt( "12345678" ) ),
            Arguments.arguments( "1234_5678_9012_3456L", Long.valueOf( "1234567890123456" ) ),
            Arguments.arguments( "0xFF_EC_DE_5EL", Long.valueOf( "FFECDE5E", 16 ) ),
            Arguments.arguments( "0x7fff_ffff_ffff_ffffL", Long.valueOf( "7fffffffffffffff", 16 ) ),
            Arguments.arguments( "(byte) 0b0010_0101 ", Byte.parseByte( "00100101", 2 ) ),
            Arguments.arguments( "5_______2", Integer.parseInt( "52" ) ),
            Arguments.arguments( "0x5_2", Integer.parseInt( "52", 16 ) ),
            Arguments.arguments( "0_52", Integer.parseInt( "52", 8 ) ),
            Arguments.arguments( "05_2", Integer.parseInt( "52", 8 ) ) );
    }

    @ParameterizedTest
    @MethodSource
    void expressions( String expression, Object expected )
    {
        String source = "@Annotation(\n" + expression + "\n) class Foo {}";
        try
        {
            builder.addSource( new StringReader( source ) );
            assertAnnotationValue( expected );
        }
        catch ( ParseException pe )
        {
            fail( pe.getMessage() + "Failed to parse '" + source + "'" );
        }
    }

    protected void assertAnnotationValue( Object expected )
    {
        JavaClass clazz = builder.getClassByName( "Foo" );
        assertEquals( 1, clazz.getAnnotations().size(), "Annotations" );
        JavaAnnotation annotation = clazz.getAnnotations().get( 0 );
        assertEquals( "Annotation", annotation.getType().getFullyQualifiedName(), "Annotation name" );
        assertEquals( 1, annotation.getPropertyMap().size(), "Properties" );

        AnnotationValue value = annotation.getProperty( "value" );
        Object v = value.accept( new EvaluatingVisitor() );
        assertEquals( expected, v, "Value" );
    }
}