/*
 * Copyright 2013 The Error Prone Authors.
 *
 * 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 com.google.errorprone.refaster;

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.ImmutableClassToInstanceMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Flags;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/**
 * Tests for {@link UTemplater#createTemplate} against real compiled code.
 *
 * @author lowasser@google.com (Louis Wasserman)
 */
@RunWith(JUnit4.class)
public class TemplatingTest extends CompilerBasedTest {

  @Test
  public void arrayAccess() {
    compile(
        "class ArrayAccessExample {",
        "  public double example(double[] array) {",
        "    return array[5];",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of("array", UArrayType.create(UPrimitiveType.DOUBLE)),
                UArrayAccess.create(UFreeIdent.create("array"), ULiteral.intLit(5)),
                UPrimitiveType.DOUBLE));
  }

  @Test
  public void binary() {
    compile(
        "class BinaryExample {",
        "  public int example(int x, int y) {",
        "    return (x + y) / 2;",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of(
                    "x", UPrimitiveType.INT,
                    "y", UPrimitiveType.INT),
                UBinary.create(
                    Kind.DIVIDE,
                    UParens.create(
                        UBinary.create(Kind.PLUS, UFreeIdent.create("x"), UFreeIdent.create("y"))),
                    ULiteral.intLit(2)),
                UPrimitiveType.INT));
  }

  @Test
  public void conditional() {
    compile(
        "class ConditionalExample {",
        "  public String example(boolean foo) {",
        "    return foo ? null : \"bar\";",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of("foo", UPrimitiveType.BOOLEAN),
                UConditional.create(
                    UFreeIdent.create("foo"), ULiteral.nullLit(), ULiteral.stringLit("bar")),
                UClassType.create("java.lang.String")));
  }

  @Test
  public void whileLoop() {
    compile(
        "import java.util.Iterator;",
        "class WhileLoopExample {",
        "  public void example(Iterator<?> itr) {",
        "    while (itr.hasNext()) {",
        "      itr.next();",
        "    }",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                ImmutableMap.of(
                    "itr", UClassType.create("java.util.Iterator", UWildcardType.create())),
                UWhileLoop.create(
                    UParens.create(
                        UMethodInvocation.create(
                            UMemberSelect.create(
                                UFreeIdent.create("itr"),
                                "hasNext",
                                UMethodType.create(UPrimitiveType.BOOLEAN)))),
                    UBlock.create(
                        UExpressionStatement.create(
                            UMethodInvocation.create(
                                UMemberSelect.create(
                                    UFreeIdent.create("itr"),
                                    "next",
                                    UMethodType.create(UTypeVar.create("E")))))))));
  }

  @Test
  public void fullyQualifiedGlobalIdent() {
    compile(
        "class FullyQualifiedIdentExample {",
        "  public java.math.RoundingMode example() {",
        "    return java.math.RoundingMode.FLOOR;",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                UStaticIdent.create(
                    "java.math.RoundingMode", "FLOOR", UClassType.create("java.math.RoundingMode")),
                UClassType.create("java.math.RoundingMode")));
  }

  @Test
  public void qualifiedGlobalIdent() {
    compile(
        "import java.math.RoundingMode;",
        "class QualifiedIdentExample {",
        "  public RoundingMode example() {",
        "    return RoundingMode.FLOOR;",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                UStaticIdent.create(
                    "java.math.RoundingMode", "FLOOR", UClassType.create("java.math.RoundingMode")),
                UClassType.create("java.math.RoundingMode")));
  }

  @Test
  public void staticImportedGlobalIdent() {
    compile(
        "import java.math.RoundingMode;",
        "import static java.math.RoundingMode.FLOOR;",
        "class StaticImportedIdentExample {",
        "  public RoundingMode example() {",
        "    return FLOOR;",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                UStaticIdent.create(
                    "java.math.RoundingMode", "FLOOR", UClassType.create("java.math.RoundingMode")),
                UClassType.create("java.math.RoundingMode")));
  }

  @Test
  public void staticMethodInvocation() {
    compile(
        "import java.math.BigInteger;",
        "class StaticMethodExample {",
        "  public BigInteger example(int x) {",
        "    return BigInteger.valueOf(x);",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of("x", UPrimitiveType.INT),
                UMethodInvocation.create(
                    UStaticIdent.create(
                        "java.math.BigInteger",
                        "valueOf",
                        UMethodType.create(
                            UClassType.create("java.math.BigInteger"), UPrimitiveType.LONG)),
                    UFreeIdent.create("x")),
                UClassType.create("java.math.BigInteger")));
  }

  @Test
  public void instanceMethodInvocation() {
    compile(
        "class InstanceMethodExample {",
        "  public char example(String str) {",
        "    return str.charAt(5);",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of("str", UClassType.create("java.lang.String")),
                UMethodInvocation.create(
                    UMemberSelect.create(
                        UFreeIdent.create("str"),
                        "charAt",
                        UMethodType.create(UPrimitiveType.CHAR, UPrimitiveType.INT)),
                    ULiteral.intLit(5)),
                UPrimitiveType.CHAR));
  }

  @Test
  public void anyOf() {
    compile(
        "import com.google.errorprone.refaster.Refaster;",
        "class InstanceMethodExample {",
        "  public char example(String str) {",
        "    return str.charAt(Refaster.anyOf(1, 3, 5));",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of("str", UClassType.create("java.lang.String")),
                UMethodInvocation.create(
                    UMemberSelect.create(
                        UFreeIdent.create("str"),
                        "charAt",
                        UMethodType.create(UPrimitiveType.CHAR, UPrimitiveType.INT)),
                    UAnyOf.create(ULiteral.intLit(1), ULiteral.intLit(3), ULiteral.intLit(5))),
                UPrimitiveType.CHAR));
  }

  @Test
  public void unary() {
    compile(
        "import java.util.Arrays;",
        "class UnaryExample {",
        "  public int example(int[] array, int key) {",
        "    return ~Arrays.binarySearch(array, key);",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of(
                    "array", UArrayType.create(UPrimitiveType.INT), "key", UPrimitiveType.INT),
                UUnary.create(
                    Kind.BITWISE_COMPLEMENT,
                    UMethodInvocation.create(
                        UStaticIdent.create(
                            "java.util.Arrays",
                            "binarySearch",
                            UMethodType.create(
                                UPrimitiveType.INT,
                                UArrayType.create(UPrimitiveType.INT),
                                UPrimitiveType.INT)),
                        UFreeIdent.create("array"),
                        UFreeIdent.create("key"))),
                UPrimitiveType.INT));
  }

  @Test
  public void genericTemplate() {
    compile(
        "import java.util.List;",
        "class GenericTemplateExample {",
        "  public <E> E example(List<E> list) {",
        "    return list.get(0);",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableClassToInstanceMap.of(),
                ImmutableList.of(UTypeVar.create("E")),
                ImmutableMap.of("list", UClassType.create("java.util.List", UTypeVar.create("E"))),
                UMethodInvocation.create(
                    UMemberSelect.create(
                        UFreeIdent.create("list"),
                        "get",
                        UMethodType.create(UTypeVar.create("E"), UPrimitiveType.INT)),
                    ULiteral.intLit(0)),
                UTypeVar.create("E")));
  }

  @Test
  public void genericMethodInvocation() {
    compile(
        "import java.util.Collections;",
        "import java.util.List;",
        "class GenericTemplateExample {",
        "  public <E> List<E> example(List<E> list) {",
        "    return Collections.unmodifiableList(list);",
        "  }",
        "}");
    UTypeVar tVar = UTypeVar.create("T");
    UTypeVar eVar = UTypeVar.create("E");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableClassToInstanceMap.of(),
                ImmutableList.of(eVar),
                ImmutableMap.of("list", UClassType.create("java.util.List", eVar)),
                UMethodInvocation.create(
                    UStaticIdent.create(
                        "java.util.Collections",
                        "unmodifiableList",
                        UForAll.create(
                            ImmutableList.of(tVar),
                            UMethodType.create(
                                UClassType.create("java.util.List", tVar),
                                UClassType.create(
                                    "java.util.List",
                                    UWildcardType.create(BoundKind.EXTENDS, tVar))))),
                    UFreeIdent.create("list")),
                UClassType.create("java.util.List", eVar)));
  }

  @Test
  public void doWhile() {
    compile(
        "class DoWhileExample {",
        "  public void example(String str) {",
        "    int old = 0;",
        "    do {",
        "      old = str.indexOf(' ', old + 1);",
        "    } while (old != -1);",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                ImmutableMap.of("str", UClassType.create("java.lang.String")),
                UVariableDecl.create("old", UPrimitiveTypeTree.INT, ULiteral.intLit(0)),
                UDoWhileLoop.create(
                    UBlock.create(
                        UExpressionStatement.create(
                            UAssign.create(
                                ULocalVarIdent.create("old"),
                                UMethodInvocation.create(
                                    UMemberSelect.create(
                                        UFreeIdent.create("str"),
                                        "indexOf",
                                        UMethodType.create(
                                            UPrimitiveType.INT,
                                            UPrimitiveType.INT,
                                            UPrimitiveType.INT)),
                                    ULiteral.charLit(' '),
                                    UBinary.create(
                                        Kind.PLUS,
                                        ULocalVarIdent.create("old"),
                                        ULiteral.intLit(1)))))),
                    UParens.create(
                        UBinary.create(
                            Kind.NOT_EQUAL_TO,
                            ULocalVarIdent.create("old"),
                            ULiteral.intLit(-1))))));
  }

  @Test
  public void voidReturn() {
    compile(
        "import java.security.MessageDigest;",
        "class VoidExample {",
        "  public void example(MessageDigest md, byte[] bytes) {",
        "    md.update(bytes);",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                ImmutableMap.of(
                    "md", UClassType.create("java.security.MessageDigest"),
                    "bytes", UArrayType.create(UPrimitiveType.BYTE)),
                UExpressionStatement.create(
                    UMethodInvocation.create(
                        UMemberSelect.create(
                            UFreeIdent.create("md"),
                            "update",
                            UMethodType.create(
                                UPrimitiveType.VOID, UArrayType.create(UPrimitiveType.BYTE))),
                        UFreeIdent.create("bytes")))));
  }

  @Test
  public void arrayCast() {
    compile(
        "class ArrayCastExample {",
        "  public String[] example(Object o) {",
        "    return (String[]) o;",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of("o", UClassType.create("java.lang.Object")),
                UTypeCast.create(
                    UArrayTypeTree.create(UClassIdent.create("java.lang.String")),
                    UFreeIdent.create("o")),
                UArrayType.create(UClassType.create("java.lang.String"))));
  }

  @Test
  public void primitiveCast() {
    compile(
        "class PrimitiveCastExample {",
        "  public char example(int x) {",
        "    return (char) x;",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of("x", UPrimitiveType.INT),
                UTypeCast.create(UPrimitiveTypeTree.CHAR, UFreeIdent.create("x")),
                UPrimitiveType.CHAR));
  }

  @Test
  public void objectCast() {
    compile(
        "class ObjectCastExample {",
        "  public String example(Object o) {",
        "    return (String) o;",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of("o", UClassType.create("java.lang.Object")),
                UTypeCast.create(UClassIdent.create("java.lang.String"), UFreeIdent.create("o")),
                UClassType.create("java.lang.String")));
  }

  @Test
  public void constructor() {
    compile(
        "import java.util.ArrayList;",
        "class ConstructorExample {",
        "  public String example() {",
        "    return new String(\"foo\");",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                UNewClass.create(UClassIdent.create("java.lang.String"), ULiteral.stringLit("foo")),
                UClassType.create("java.lang.String")));
  }

  @Test
  public void forLoop() {
    compile(
        "class ForLoopExample {",
        "  public void example(int from, int to) {",
        "     for (int i = from; i < to; i++) {",
        "     }",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                ImmutableMap.of(
                    "from", UPrimitiveType.INT,
                    "to", UPrimitiveType.INT),
                UForLoop.create(
                    ImmutableList.of(
                        UVariableDecl.create(
                            "i", UPrimitiveTypeTree.INT, UFreeIdent.create("from"))),
                    UBinary.create(
                        Kind.LESS_THAN, ULocalVarIdent.create("i"), UFreeIdent.create("to")),
                    ImmutableList.of(
                        UExpressionStatement.create(
                            UUnary.create(Kind.POSTFIX_INCREMENT, ULocalVarIdent.create("i")))),
                    UBlock.create())));
  }

  @Test
  public void forLoopNoStep() {
    compile(
        "class ForLoopExample {",
        "  public void example(int from, int to) {",
        "     for (int i = from; i < to;) {",
        "     }",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                ImmutableMap.of(
                    "from", UPrimitiveType.INT,
                    "to", UPrimitiveType.INT),
                UForLoop.create(
                    ImmutableList.of(
                        UVariableDecl.create(
                            "i", UPrimitiveTypeTree.INT, UFreeIdent.create("from"))),
                    UBinary.create(
                        Kind.LESS_THAN, ULocalVarIdent.create("i"), UFreeIdent.create("to")),
                    ImmutableList.<UExpressionStatement>of(),
                    UBlock.create())));
  }

  @Test
  public void forLoopNoCondition() {
    compile(
        "class ForLoopExample {",
        "  public void example(int from, int to) {",
        "     for (int i = from; ; i++) {",
        "     }",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                ImmutableMap.of(
                    "from", UPrimitiveType.INT,
                    "to", UPrimitiveType.INT),
                UForLoop.create(
                    ImmutableList.of(
                        UVariableDecl.create(
                            "i", UPrimitiveTypeTree.INT, UFreeIdent.create("from"))),
                    null,
                    ImmutableList.of(
                        UExpressionStatement.create(
                            UUnary.create(Kind.POSTFIX_INCREMENT, ULocalVarIdent.create("i")))),
                    UBlock.create())));
  }

  @Test
  public void forLoopNoStart() {
    compile(
        "class ForLoopExample {",
        "  public void example(int from, int to) {",
        "     for (; from < to; from++) {",
        "     }",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                ImmutableMap.of(
                    "from", UPrimitiveType.INT,
                    "to", UPrimitiveType.INT),
                UForLoop.create(
                    ImmutableList.<UStatement>of(),
                    UBinary.create(
                        Kind.LESS_THAN, UFreeIdent.create("from"), UFreeIdent.create("to")),
                    ImmutableList.of(
                        UExpressionStatement.create(
                            UUnary.create(Kind.POSTFIX_INCREMENT, UFreeIdent.create("from")))),
                    UBlock.create())));
  }

  @Test
  public void parameterizedCast() {
    compile(
        "import java.util.Collection;",
        "import java.util.List;",
        "class ParameterizedCastExample {",
        "  public List<String> example(Collection<String> elements) {",
        "    return (List<String>) elements;",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of(
                    "elements",
                    UClassType.create(
                        "java.util.Collection", UClassType.create("java.lang.String"))),
                UTypeCast.create(
                    UTypeApply.create(
                        UClassIdent.create("java.util.List"),
                        UClassIdent.create("java.lang.String")),
                    UFreeIdent.create("elements")),
                UClassType.create("java.util.List", UClassType.create("java.lang.String"))));
  }

  @Test
  public void instanceOf() {
    compile(
        "class InstanceOfExample {",
        "  public boolean example(Object foo) {",
        "    return foo instanceof CharSequence;",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of("foo", UClassType.create("java.lang.Object")),
                UInstanceOf.create(
                    UFreeIdent.create("foo"), UClassIdent.create("java.lang.CharSequence")),
                UPrimitiveType.BOOLEAN));
  }

  @Test
  public void assignment() {
    compile(
        "class AssignmentExample {",
        "  public String example(String foo) {",
        "    return foo = \"bar\";",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableMap.of("foo", UClassType.create("java.lang.String")),
                UAssign.create(UFreeIdent.create("foo"), ULiteral.stringLit("bar")),
                UClassType.create("java.lang.String")));
  }

  @Test
  public void recursiveTypes() {
    compile(
        "class RecursiveTypeExample {",
        "  public <E extends Enum<E>> E example(E e) {",
        "    return e;",
        "  }",
        "}");
    Template<?> template = UTemplater.createTemplate(context, getMethodDeclaration("example"));
    UTypeVar eVar = Iterables.getOnlyElement(template.templateTypeVariables());
    assertThat(template)
        .isEqualTo(
            ExpressionTemplate.create(
                ImmutableClassToInstanceMap.of(),
                ImmutableList.of(UTypeVar.create("E", UClassType.create("java.lang.Enum", eVar))),
                ImmutableMap.of("e", eVar),
                UFreeIdent.create("e"),
                eVar));
  }

  @Test
  public void localVariable() {
    compile(
        "import java.util.ArrayList;",
        "import java.util.Comparator;",
        "import java.util.Collection;",
        "import java.util.Collections;",
        "import java.util.List;",
        "class LocalVariableExample {",
        "  public <E> void example(Collection<E> collection, Comparator<? super E> comparator) {",
        "    List<E> list = new ArrayList<E>(collection);",
        "    Collections.sort(list, comparator);",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                ImmutableList.of(UTypeVar.create("E")),
                ImmutableMap.of(
                    "collection", UClassType.create("java.util.Collection", UTypeVar.create("E")),
                    "comparator",
                        UClassType.create(
                            "java.util.Comparator",
                            UWildcardType.create(BoundKind.SUPER, UTypeVar.create("E")))),
                UVariableDecl.create(
                    "list",
                    UTypeApply.create("java.util.List", UTypeVarIdent.create("E")),
                    UNewClass.create(
                        UTypeApply.create("java.util.ArrayList", UTypeVarIdent.create("E")),
                        UFreeIdent.create("collection"))),
                UExpressionStatement.create(
                    UMethodInvocation.create(
                        UStaticIdent.create(
                            "java.util.Collections",
                            "sort",
                            UForAll.create(
                                ImmutableList.of(UTypeVar.create("T")),
                                UMethodType.create(
                                    UPrimitiveType.VOID,
                                    UClassType.create("java.util.List", UTypeVar.create("T")),
                                    UClassType.create(
                                        "java.util.Comparator",
                                        UWildcardType.create(
                                            BoundKind.SUPER, UTypeVar.create("T")))))),
                        ULocalVarIdent.create("list"),
                        UFreeIdent.create("comparator")))));
  }

  @Test
  public void throwException() {
    compile(
        "class ThrowExceptionExample {",
        "  public void example() {",
        "    throw new IllegalArgumentException();",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                UThrow.create(
                    UNewClass.create(UClassIdent.create("java.lang.IllegalArgumentException")))));
  }

  @Test
  public void forEach() {
    compile(
        "class ForEachExample {",
        "  public void example(int[] array) {",
        "    int sum = 0;",
        "    for (int value : array) {",
        "      sum += value;",
        "    }",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                ImmutableMap.of("array", UArrayType.create(UPrimitiveType.INT)),
                UVariableDecl.create("sum", UPrimitiveTypeTree.INT, ULiteral.intLit(0)),
                UEnhancedForLoop.create(
                    UVariableDecl.create("value", UPrimitiveTypeTree.INT),
                    UFreeIdent.create("array"),
                    UBlock.create(
                        UExpressionStatement.create(
                            UAssignOp.create(
                                ULocalVarIdent.create("sum"),
                                Kind.PLUS_ASSIGNMENT,
                                ULocalVarIdent.create("value")))))));
  }

  @Test
  public void synchronizd() {
    compile(
        "class SynchronizedExample {",
        "  public void example(int[] array) {",
        "    int sum = 0;",
        "    synchronized (array) {",
        "      for (int value : array) {",
        "        sum += value;",
        "      }",
        "    }",
        "  }",
        "}");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                ImmutableMap.of("array", UArrayType.create(UPrimitiveType.INT)),
                UVariableDecl.create("sum", UPrimitiveTypeTree.INT, ULiteral.intLit(0)),
                USynchronized.create(
                    UParens.create(UFreeIdent.create("array")),
                    UBlock.create(
                        UEnhancedForLoop.create(
                            UVariableDecl.create("value", UPrimitiveTypeTree.INT),
                            UFreeIdent.create("array"),
                            UBlock.create(
                                UExpressionStatement.create(
                                    UAssignOp.create(
                                        ULocalVarIdent.create("sum"),
                                        Kind.PLUS_ASSIGNMENT,
                                        ULocalVarIdent.create("value")))))))));
  }

  @Test
  public void anonymousClass() {
    compile(
        "import java.util.Collections;",
        "import java.util.Comparator;",
        "import java.util.List;",
        "class AnonymousClassExample {",
        "  public void example(List<String> list) {",
        "    Collections.sort(list, new Comparator<String>() {",
        "      @Override public int compare(String a, String b) {",
        "        return a.compareTo(b);",
        "      }",
        "    });",
        "  }",
        "}");
    UTypeVar tVar = UTypeVar.create("T");
    assertThat(UTemplater.createTemplate(context, getMethodDeclaration("example")))
        .isEqualTo(
            BlockTemplate.create(
                ImmutableMap.of(
                    "list",
                    UClassType.create("java.util.List", UClassType.create("java.lang.String"))),
                UExpressionStatement.create(
                    UMethodInvocation.create(
                        UStaticIdent.create(
                            "java.util.Collections",
                            "sort",
                            UForAll.create(
                                ImmutableList.of(tVar),
                                UMethodType.create(
                                    UPrimitiveType.VOID,
                                    UClassType.create("java.util.List", tVar),
                                    UClassType.create(
                                        "java.util.Comparator",
                                        UWildcardType.create(BoundKind.SUPER, tVar))))),
                        UFreeIdent.create("list"),
                        UNewClass.create(
                            null,
                            ImmutableList.<UExpression>of(),
                            UTypeApply.create(
                                "java.util.Comparator", UClassIdent.create("java.lang.String")),
                            ImmutableList.<UExpression>of(),
                            UClassDecl.create(
                                UMethodDecl.create(
                                    UModifiers.create(
                                        Flags.PUBLIC,
                                        UAnnotation.create(
                                            UClassIdent.create("java.lang.Override"))),
                                    "compare",
                                    UPrimitiveTypeTree.INT,
                                    ImmutableList.of(
                                        UVariableDecl.create(
                                            "a", UClassIdent.create("java.lang.String")),
                                        UVariableDecl.create(
                                            "b", UClassIdent.create("java.lang.String"))),
                                    ImmutableList.<UExpression>of(),
                                    UBlock.create(
                                        UReturn.create(
                                            UMethodInvocation.create(
                                                UMemberSelect.create(
                                                    ULocalVarIdent.create("a"),
                                                    "compareTo",
                                                    UMethodType.create(
                                                        UPrimitiveType.INT,
                                                        UClassType.create("java.lang.String"))),
                                                ULocalVarIdent.create("b")))))))))));
  }
}
