/*
* Copyright (C) 2011 Red Hat, Inc. and/or its affiliates.
*
* 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 org.jboss.errai.codegen.test;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.util.TypeLiteral;
import javax.inject.Inject;
import org.jboss.errai.codegen.AssignmentOperator;
import org.jboss.errai.codegen.Cast;
import org.jboss.errai.codegen.Context;
import org.jboss.errai.codegen.Statement;
import org.jboss.errai.codegen.Variable;
import org.jboss.errai.codegen.VariableReference;
import org.jboss.errai.codegen.builder.ContextualStatementBuilder;
import org.jboss.errai.codegen.builder.impl.ObjectBuilder;
import org.jboss.errai.codegen.builder.impl.StatementBuilder;
import org.jboss.errai.codegen.exception.InvalidTypeException;
import org.jboss.errai.codegen.exception.OutOfScopeException;
import org.jboss.errai.codegen.exception.UndefinedFieldException;
import org.jboss.errai.codegen.literal.LiteralFactory;
import org.jboss.errai.codegen.meta.MetaClassFactory;
import org.jboss.errai.codegen.test.model.BeanWithTypeParmedMeths;
import org.jboss.errai.codegen.test.model.Foo;
import org.jboss.errai.codegen.test.model.TEnum;
import org.jboss.errai.codegen.util.Bitwise;
import org.jboss.errai.codegen.util.Bool;
import org.jboss.errai.codegen.util.Expr;
import org.jboss.errai.codegen.util.Refs;
import org.jboss.errai.codegen.util.Stmt;
import org.junit.Assert;
import org.junit.Test;
/**
* Tests the {@link StatementBuilder} API.
*
* @author Christian Sadilek <csadilek@redhat.com>
*/
public class StatementBuilderTest extends AbstractCodegenTest {
@Test
public void testDeclareVariableWithExactTypeProvided() {
final Context ctx = Context.create();
final String s = StatementBuilder.create().declareVariable("n", Integer.class, 10).generate(ctx);
assertEquals("failed to generate variable declaration with type provided",
"Integer n = 10;", s);
final VariableReference n = ctx.getVariable("n");
assertEquals("Wrong variable name", "n", n.getName());
Assert.assertEquals("Wrong variable type", MetaClassFactory.get(Integer.class), n.getType());
Assert.assertEquals("Wrong variable value", LiteralFactory.getLiteral(10), n.getValue());
}
@Test
public void testDeclareVariableWithIntegerTypeInference() {
final Context ctx = Context.create();
final String s = StatementBuilder.create().declareVariable("n", 10).generate(ctx);
assertEquals("failed to generate variable declaration with Integers type inference",
"Integer n = 10;", s);
final VariableReference n = ctx.getVariable("n");
assertEquals("Wrong variable name", "n", n.getName());
Assert.assertEquals("Wrong variable type", MetaClassFactory.get(Integer.class), n.getType());
Assert.assertEquals("Wrong variable value", LiteralFactory.getLiteral(10), n.getValue());
}
@Test
public void testDeclareVariableWithStringTypeInference() {
final Context ctx = Context.create();
final String s = StatementBuilder.create().declareVariable("n", "10").generate(ctx);
assertEquals("failed to generate variable declaration with =String type inference",
"String n = \"10\";", s);
final VariableReference n = ctx.getVariable("n");
assertEquals("Wrong variable name", "n", n.getName());
Assert.assertEquals("Wrong variable type", MetaClassFactory.get(String.class), n.getType());
Assert.assertEquals("Wrong variable value", LiteralFactory.getLiteral("10"), n.getValue());
}
@Test
public void testDeclareVariableWithImplicitTypeConversion() {
final Context ctx = Context.create();
final String s = StatementBuilder.create().declareVariable("n", Integer.class, "10").generate(ctx);
assertEquals("failed to generate variable declaration with implicit type conversion",
"Integer n = 10;", s);
final VariableReference n = ctx.getVariable("n");
assertEquals("Wrong variable name", "n", n.getName());
Assert.assertEquals("Wrong variable type", MetaClassFactory.get(Integer.class), n.getType());
Assert.assertEquals("Wrong variable value", LiteralFactory.getLiteral(10), n.getValue());
try {
StatementBuilder.create().declareVariable("n", Integer.class, "abc").toJavaString();
fail("Expected InvalidTypeException");
}
catch (InvalidTypeException ive) {
// expected
assertTrue(ive.getCause() instanceof NumberFormatException);
}
}
@Test
public void testDeclareVariableWithObjectInitializationWithExactTypeProvided() {
final Context ctx = Context.create();
final String s = StatementBuilder.create().declareVariable("str", String.class,
ObjectBuilder.newInstanceOf(String.class)).generate(ctx);
assertEquals("failed to generate variable declaration with object initialization and type provided",
"String str = new String();", s);
final VariableReference str = ctx.getVariable("str");
assertEquals("Wrong variable name", "str", str.getName());
Assert.assertEquals("Wrong variable type", MetaClassFactory.get(String.class), str.getType());
}
@Test
public void testDeclareVariableWithObjectInitializationWithStringTypeInference() {
final Context ctx = Context.create();
final String s = StatementBuilder.create(ctx)
.declareVariable("str", ObjectBuilder.newInstanceOf(String.class)).toJavaString();
assertEquals("failed to generate variable declaration with object initialization and string type inference",
"String str = new String();", s);
final VariableReference str = ctx.getVariable("str");
assertEquals("Wrong variable name", "str", str.getName());
Assert.assertEquals("Wrong variable type", MetaClassFactory.get(String.class), str.getType());
}
@Test
public void testDeclareVariableWithStatementInitialization() {
final Context ctx = Context.create();
final String s = Stmt.declareVariable("str", String.class,
Stmt.nestedCall(Stmt.newObject(Integer.class).withParameters(2)).invoke("toString"))
.generate(ctx);
assertEquals("failed to generate variable declaration with statement initialization",
"String str = new Integer(2).toString();", s);
final VariableReference str = ctx.getVariable("str");
assertEquals("Wrong variable name", "str", str.getName());
Assert.assertEquals("Wrong variable type", MetaClassFactory.get(String.class), str.getType());
}
@Test
public void testDeclareFinalVariable() {
final Context ctx = Context.create();
final String s = StatementBuilder.create(ctx)
.declareVariable(String.class).asFinal().named("str").initializeWith("10").toJavaString();
assertEquals("failed to generate final variable declaration", "final String str = \"10\";", s);
final VariableReference str = ctx.getVariable("str");
assertEquals("Wrong variable name", "str", str.getName());
Assert.assertTrue("Variable should be final", ctx.getVariables().get("str").isFinal());
Assert.assertEquals("Wrong variable type", MetaClassFactory.get(String.class), str.getType());
}
@Test
public void testLoadUndefinedVariable() {
try {
StatementBuilder.create().loadVariable("n").toJavaString();
fail("Expected OutOfScopeException");
}
catch (OutOfScopeException e) {
// expected
}
}
@Test
public void testCreateAndInitializeArray() {
final String s = StatementBuilder.create().newArray(String.class).initialize("1", "2").toJavaString();
assertEquals("Failed to generate 1-dimensional String array", "new String[] { \"1\", \"2\" }", s);
}
@Test
public void testCreateAndInitializeArrayWithInvalidInitialization() {
try {
StatementBuilder.create().newArray(Annotation.class)
.initialize("1", "2")
.toJavaString();
fail("Expected InvalidTypeException");
}
catch (InvalidTypeException oose) {
// expected
}
}
@Test
public void testCreateAndInitializeArrayWithMissingInitializationAndDimensions() {
try {
StatementBuilder.create().newArray(String.class).toJavaString();
fail("Expected RuntimeException");
}
catch (Exception e) {
// expected
assertEquals("Wrong exception details",
"Must provide either dimension expressions or an array initializer", e.getMessage());
}
}
@Test
public void testCreateAndInitializeAnnotationArray() {
final Statement annotation1 = ObjectBuilder.newInstanceOf(Annotation.class)
.extend()
.publicOverridesMethod("annotationType")
.append(StatementBuilder.create().load(Inject.class).returnValue())
.finish()
.finish();
final Statement annotation2 = ObjectBuilder.newInstanceOf(Annotation.class)
.extend()
.publicOverridesMethod("annotationType")
.append(StatementBuilder.create().load(PostConstruct.class).returnValue())
.finish()
.finish();
final String s = StatementBuilder.create().newArray(Annotation.class)
.initialize(annotation1, annotation2)
.toJavaString();
assertEquals("failed to generate Annotation array",
"new java.lang.annotation.Annotation[] { " +
"new java.lang.annotation.Annotation() {\n" +
" public Class annotationType() {\n" +
" return javax.inject.Inject.class;\n" +
" }\n" +
"}" +
", new java.lang.annotation.Annotation() {\n" +
" public Class annotationType() {\n" +
" return javax.annotation.PostConstruct.class;\n" +
" }\n" +
" }\n" +
"}", s);
}
@Test
@SuppressWarnings(value = { "all" })
public void testCreateAndInitializeTwoDimensionalArray() {
final String s = StatementBuilder.create().newArray(Integer.class)
.initialize(new Integer[][] { { 1, 2 }, { 3, 4 } })
.toJavaString();
assertEquals("Failed to generate two dimensional array", "new Integer[][] { { 1, 2 }, { 3, 4 } }", s);
}
@Test
@SuppressWarnings(value = { "all" })
public void testCreateAndInitializeTwoDimensionalArrayWithSingleValue() {
final String s = StatementBuilder.create().newArray(Integer.class)
.initialize(new Object[][] { { 1, 2 } })
.toJavaString();
assertEquals("Failed to generate two dimensional array", "new Integer[][] { { 1, 2 } }", s);
}
@Test
@SuppressWarnings(value = { "all" })
public void testCreateAndInitializeTwoDimensionalObjectArrayWithIntegers() {
final String s = StatementBuilder.create().newArray(Object.class)
.initialize(new Object[][] { { 1, 2 } })
.toJavaString();
assertEquals("Failed to generate two dimensional array", "new Object[][] { { 1, 2 } }", s);
}
@Test
@SuppressWarnings(value = { "all" })
public void testCreateAndInitializeTwoDimensionalArrayWithStatements() {
final String s = StatementBuilder.create().newArray(String.class)
.initialize(new Statement[][] {
{ StatementBuilder.create().invokeStatic(Integer.class, "toString", 1),
StatementBuilder.create().invokeStatic(Integer.class, "toString", 2) },
{ StatementBuilder.create().invokeStatic(Integer.class, "toString", 3),
StatementBuilder.create().invokeStatic(Integer.class, "toString", 4) } })
.toJavaString();
assertEquals("Failed to generate two dimensional array using statements",
"new String[][] { { Integer.toString(1), Integer.toString(2) }, " +
"{ Integer.toString(3), Integer.toString(4) } }", s);
}
@Test
@SuppressWarnings(value = { "all" })
public void testCreateAndInitializeTwoDimensionalArrayWithStatementsAndLiterals() {
final String s = StatementBuilder.create().newArray(String.class)
.initialize(new Object[][] {
{ StatementBuilder.create().invokeStatic(Integer.class, "toString", 1), "2" },
{ StatementBuilder.create().invokeStatic(Integer.class, "toString", 3), "4" } })
.toJavaString();
assertEquals("Failed to generate two dimensional array using statements and objects",
"new String[][] { { Integer.toString(1), \"2\" }," +
" { Integer.toString(3), \"4\" } }", s);
}
@Test
@SuppressWarnings(value = { "all" })
public void testCreateAndInitializeThreeDimensionalArray() {
final String s = StatementBuilder.create().newArray(String.class)
.initialize(new String[][][] { { { "1", "2" }, { "a", "b" } }, { { "3", "4" }, { "b", "c" } } })
.toJavaString();
assertEquals("Failed to generate three dimensional array",
"new String[][][] { { { \"1\", \"2\" }, { \"a\", \"b\" } }, { { \"3\", \"4\" }, { \"b\", \"c\" } } }", s);
}
@Test
public void testAssignArrayValue() {
final String s = StatementBuilder.create()
.declareVariable("twoDimArray", String[][].class)
.loadVariable("twoDimArray", 1, 2)
.assignValue("test")
.toJavaString();
assertEquals("Failed to generate array assignment", "twoDimArray[1][2] = \"test\";", s);
}
@Test
public void testAssignArrayValueWithPreIncrementAssignment() {
final String s = StatementBuilder.create()
.declareVariable("twoDimArray", String[][].class)
.loadVariable("twoDimArray", 1, 2)
.assignValue(AssignmentOperator.PreIncrementAssign, "test")
.toJavaString();
assertEquals("Failed to generate array assignment", "twoDimArray[1][2] += \"test\";", s);
}
@Test
public void testAssignArrayValueWithVariableIndexes() {
final String s = StatementBuilder.create()
.declareVariable("twoDimArray", String[][].class)
.declareVariable("i", int.class)
.declareVariable("j", int.class)
.loadVariable("twoDimArray", Variable.get("i"), Variable.get("j"))
.assignValue("test")
.toJavaString();
assertEquals("Failed to generate array assignment", "twoDimArray[i][j] = \"test\";", s);
}
@Test
public void testAssignArrayValueWithInvalidArray() {
try {
StatementBuilder.create()
.declareVariable("twoDimArray", String.class)
.loadVariable("twoDimArray", 1, 2)
.assignValue("test")
.toJavaString();
fail("Expected InvalidTypeExcpetion");
}
catch (InvalidTypeException e) {
// Expected, variable is not an array.
}
}
@Test
public void testAssignArrayValueWithInvalidIndexType() {
try {
StatementBuilder.create()
.declareVariable("twoDimArray", String[][].class)
.declareVariable("i", float.class)
.declareVariable("j", float.class)
.loadVariable("twoDimArray", Variable.get("i"), Variable.get("j"))
.assignValue("test")
.toJavaString();
fail("Expected InvalidTypeExcpetion");
}
catch (InvalidTypeException e) {
// Expected, indexes are no integers
}
}
@Test
public void testObjectCreationWithLiteralParameter() {
final String s = StatementBuilder.create().newObject(String.class).withParameters("original").toJavaString();
assertEquals("failed to generate new object with parameters", "new String(\"original\")", s);
}
@Test
public void testObjectCreationWithVariableParameter() {
final String s = StatementBuilder.create()
.declareVariable("original", String.class)
.newObject(String.class).withParameters(Variable.get("original")).toJavaString();
assertEquals("failed to generate new object with parameters", "new String(original)", s);
}
@Test
public void testObjectCreationWithParameterizedType() {
final String s = StatementBuilder.create().newObject(new TypeLiteral<ArrayList<String>>() {
}).toJavaString();
assertEquals("failed to generate new object with parameterized type", "new java.util.ArrayList<String>()", s);
}
@Test
public void testObjectCreationWithAutoImportedParameterizedType() {
final Context c = Context.create().autoImport();
final String s = StatementBuilder.create(c).newObject(new TypeLiteral<ArrayList<Date>>() {
}).toJavaString();
assertEquals("failed to generate new object with parameterized type", "new ArrayList<Date>()", s);
}
@Test
public void testObjectCreationWithParameterizedTypeAndClassImport() {
final Context c = Context.create().addImport(MetaClassFactory.get(ArrayList.class));
final String s = StatementBuilder.create(c).newObject(new TypeLiteral<ArrayList<String>>() {
}).toJavaString();
assertEquals("failed to generate new object with parameterized type", "new ArrayList<String>()", s);
}
@Test
public void testObjectCreationWithFullyQualifiedParameterizedTypeAndClassImport() {
final Context c = Context.create().addImport(MetaClassFactory.get(ArrayList.class));
final String s = StatementBuilder.create(c).newObject(new TypeLiteral<ArrayList<Date>>() {
}).toJavaString();
assertEquals("failed to generate new object with parameterized type", "new ArrayList<java.util.Date>()", s);
}
@Test
public void testObjectCreationWithNestedParameterizedTypeAndClassImports() {
final Context c = Context.create()
.addImport(MetaClassFactory.get(ArrayList.class))
.addImport(MetaClassFactory.get(HashMap.class));
final String s = StatementBuilder.create(c)
.newObject(new TypeLiteral<ArrayList<ArrayList<HashMap<String, Integer>>>>() {
}).toJavaString();
assertEquals("failed to generate new object with parameterized type",
"new ArrayList<ArrayList<HashMap<String, Integer>>>()", s);
}
@Test
public void testObjectCreationOfUninstantiableType() {
try {
Stmt.newObject(List.class).toJavaString();
fail("Expected InvalidTypeExcpetion");
}
catch (InvalidTypeException e) {
// Expected, List is not instantiable
}
}
@Test
public void testThrowExceptionUsingNewInstance() {
final Context c = Context.create().autoImport();
final String s = StatementBuilder.create(c).throw_(InvalidTypeException.class).toJavaString();
assertEquals("failed to generate throw statement using a new instance",
"throw new InvalidTypeException()", s);
}
@Test
public void testThrowExceptionUsingNewInstanceWithParameters() {
final Context c = Context.create().autoImport();
final String s = StatementBuilder.create(c).throw_(InvalidTypeException.class, "message").toJavaString();
assertEquals("failed to generate throw statement using a new instance",
"throw new InvalidTypeException(\"message\")", s);
}
@Test
public void testThrowExceptionUsingVariable() {
final String s = StatementBuilder.create().declareVariable("t", Throwable.class).throw_("t").toJavaString();
assertEquals("failed to generate throw statement using a variable", "throw t", s);
}
@Test
public void testThrowExceptionUsingInvalidVariable() {
try {
StatementBuilder.create()
.declareVariable("t", Integer.class)
.throw_("t")
.toJavaString();
fail("expected InvalidTypeException");
}
catch (InvalidTypeException e) {
// expected
}
}
@Test
public void testThrowExceptionUsingUndefinedVariable() {
try {
StatementBuilder.create()
.throw_("t")
.toJavaString();
fail("expected OutOfScopeException");
}
catch (OutOfScopeException e) {
// expected
}
}
@Test
public void testNestedCall() {
final String s = StatementBuilder.create()
.nestedCall(
StatementBuilder.create().declareVariable("n", Integer.class).loadVariable("n").invoke("toString"))
.invoke("getBytes")
.toJavaString();
assertEquals("failed to generate nested call", "n.toString().getBytes()", s);
}
@Test
public void testAssignField() {
final String s = Stmt.create(Context.create().autoImport()).nestedCall(
Stmt.newObject(Foo.class)).loadField("bar").loadField("name").assignValue("test").toJavaString();
assertEquals("failed to generate nested field assignment",
"new Foo().bar.name = \"test\";", s);
}
@Test
public void testAssignInvalidField() {
try {
final String s = Stmt.create(Context.create().autoImport()).nestedCall(
Stmt.newObject(Foo.class))
.loadField("invalid")
.assignValue("test")
.toJavaString();
fail("expected UndefinedFieldException");
}
catch (UndefinedFieldException e) {
// expected
}
}
@Test
public void testCastDown() {
final Statement stmt = Cast.to(String.class, Stmt.declareVariable("obj", Object.class).loadVariable("obj"));
assertEquals("failed to generate cast", "(String) obj", stmt.generate(Context.create()));
}
@Test
public void testCastUp() {
final Statement stmt = Cast.to(Object.class, Stmt.declareVariable("str", String.class).loadVariable("str"));
assertEquals("created a redundant cast", "str", stmt.generate(Context.create()));
}
@Test
public void testCastWithVariableGetAPI() {
final Context ctx = Context.create();
ctx.addVariable(Variable.create("str", String.class));
final Statement stmt = Cast.to(Object.class, Variable.get("str"));
assertEquals("created a redundant cast", "str", stmt.generate(ctx));
}
@Test
public void testInvalidCast() {
try {
final Statement stmt = Cast.to(Integer.class, Stmt.declareVariable("str", String.class).loadVariable("str"));
stmt.generate(Context.create());
fail("expected InvalidTypeException");
}
catch (InvalidTypeException e) {
// expected
assertEquals("Wrong exception message", "java.lang.String cannot be cast to java.lang.Integer", e.getMessage());
}
}
@Test
public void testReturnVoid() {
final Context ctx = Context.create();
ctx.addVariable(Variable.create("foo", Object.class));
final Statement stmt = Stmt.if_(Bool.isNull(Refs.get("foo")))
.append(Stmt.returnVoid()).finish();
assertEquals("failed to generate return statement", "if (foo == null) {\n" +
" return;\n" +
"}", stmt.generate(ctx));
}
@Test
public void testTypeInferenceWorksPropertyForParameterizedMethodTypes() {
final String s =
Stmt.loadStatic(BeanWithTypeParmedMeths.class, "INSTANCE")
.invoke("setFooBarMap", Stmt.loadStatic(BeanWithTypeParmedMeths.class, "INSTANCE").invoke("getFooBarMap"))
.toJavaString();
assertEquals("org.jboss.errai.codegen.test.model.BeanWithTypeParmedMeths.INSTANCE" +
".setFooBarMap(org.jboss.errai.codegen.test.model.BeanWithTypeParmedMeths.INSTANCE.getFooBarMap())",
s);
}
@Test
public void testBitwiseOrExpression() {
final String generate = Bitwise.or(Stmt.load(1), Stmt.load(2), Stmt.load(3)).generate(Context.create());
assertEquals("1 | 2 | 3", generate);
}
@Test
public void testBitwiseAndExpression() {
final String generate = Bitwise.and(Stmt.load(1), Stmt.load(2), Stmt.load(3)).generate(Context.create());
assertEquals("1 & 2 & 3", generate);
}
@Test
public void testMixedBitwise() {
final String generate = Bitwise.or(Stmt.load(1), Stmt.load(2),
Expr.qualify(Bitwise.and(Stmt.load(10), Stmt.load(20)))).generate(Context.create());
assertEquals("1 | 2 | (10 & 20)", generate);
}
@Test
public void testPassBitwiseToMethodParameter() {
final Statement bitwiseStatement = Bitwise.or(Stmt.load(1), Stmt.load(2),
Expr.qualify(Bitwise.and(Stmt.load(10), Stmt.load(20))));
final String generate = Stmt.newObject(Integer.class).withParameters(bitwiseStatement).generate(Context.create());
assertEquals("new Integer(1 | 2 | (10 & 20))", generate);
}
@Test
public void testEnumReference() {
final ContextualStatementBuilder statementBuilder = Stmt.loadStatic(TEnum.class, "FIRST");
assertEquals(TEnum.class.getName() + ".FIRST", statementBuilder.generate(Context.create()));
}
}