All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.java.ast.parser.TreeFactory Maven / Gradle / Ivy

/*
 * SonarQube Java
 * Copyright (C) 2012 SonarSource
 * [email protected]
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.java.ast.parser;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.sonar.java.ast.api.JavaKeyword;
import org.sonar.java.ast.api.JavaPunctuator;
import org.sonar.java.ast.api.JavaTokenType;
import org.sonar.java.model.ArrayDimensionTreeImpl;
import org.sonar.java.model.InternalSyntaxToken;
import org.sonar.java.model.JavaTree;
import org.sonar.java.model.JavaTree.ArrayTypeTreeImpl;
import org.sonar.java.model.JavaTree.CompilationUnitTreeImpl;
import org.sonar.java.model.JavaTree.ImportTreeImpl;
import org.sonar.java.model.JavaTree.PackageDeclarationTreeImpl;
import org.sonar.java.model.JavaTree.ParameterizedTypeTreeImpl;
import org.sonar.java.model.JavaTree.PrimitiveTypeTreeImpl;
import org.sonar.java.model.JavaTree.UnionTypeTreeImpl;
import org.sonar.java.model.JavaTree.WildcardTreeImpl;
import org.sonar.java.model.KindMaps;
import org.sonar.java.model.TypeParameterTreeImpl;
import org.sonar.java.model.declaration.AnnotationTreeImpl;
import org.sonar.java.model.declaration.ClassTreeImpl;
import org.sonar.java.model.declaration.EnumConstantTreeImpl;
import org.sonar.java.model.declaration.MethodTreeImpl;
import org.sonar.java.model.declaration.ModifierKeywordTreeImpl;
import org.sonar.java.model.declaration.ModifiersTreeImpl;
import org.sonar.java.model.declaration.VariableTreeImpl;
import org.sonar.java.model.expression.ArrayAccessExpressionTreeImpl;
import org.sonar.java.model.expression.AssignmentExpressionTreeImpl;
import org.sonar.java.model.expression.BinaryExpressionTreeImpl;
import org.sonar.java.model.expression.ConditionalExpressionTreeImpl;
import org.sonar.java.model.expression.IdentifierTreeImpl;
import org.sonar.java.model.expression.InstanceOfTreeImpl;
import org.sonar.java.model.expression.InternalPostfixUnaryExpression;
import org.sonar.java.model.expression.InternalPrefixUnaryExpression;
import org.sonar.java.model.expression.LambdaExpressionTreeImpl;
import org.sonar.java.model.expression.LiteralTreeImpl;
import org.sonar.java.model.expression.MemberSelectExpressionTreeImpl;
import org.sonar.java.model.expression.MethodInvocationTreeImpl;
import org.sonar.java.model.expression.MethodReferenceTreeImpl;
import org.sonar.java.model.expression.NewArrayTreeImpl;
import org.sonar.java.model.expression.NewClassTreeImpl;
import org.sonar.java.model.expression.ParenthesizedTreeImpl;
import org.sonar.java.model.expression.TypeArgumentListTreeImpl;
import org.sonar.java.model.expression.TypeCastExpressionTreeImpl;
import org.sonar.java.model.statement.AssertStatementTreeImpl;
import org.sonar.java.model.statement.BlockTreeImpl;
import org.sonar.java.model.statement.BreakStatementTreeImpl;
import org.sonar.java.model.statement.CaseGroupTreeImpl;
import org.sonar.java.model.statement.CaseLabelTreeImpl;
import org.sonar.java.model.statement.CatchTreeImpl;
import org.sonar.java.model.statement.ContinueStatementTreeImpl;
import org.sonar.java.model.statement.DoWhileStatementTreeImpl;
import org.sonar.java.model.statement.EmptyStatementTreeImpl;
import org.sonar.java.model.statement.ExpressionStatementTreeImpl;
import org.sonar.java.model.statement.ForEachStatementImpl;
import org.sonar.java.model.statement.ForStatementTreeImpl;
import org.sonar.java.model.statement.IfStatementTreeImpl;
import org.sonar.java.model.statement.LabeledStatementTreeImpl;
import org.sonar.java.model.statement.ReturnStatementTreeImpl;
import org.sonar.java.model.statement.StaticInitializerTreeImpl;
import org.sonar.java.model.statement.SwitchStatementTreeImpl;
import org.sonar.java.model.statement.SynchronizedStatementTreeImpl;
import org.sonar.java.model.statement.ThrowStatementTreeImpl;
import org.sonar.java.model.statement.TryStatementTreeImpl;
import org.sonar.java.model.statement.WhileStatementTreeImpl;
import com.sonar.sslr.api.typed.Optional;
import org.sonar.plugins.java.api.tree.AnnotationTree;
import org.sonar.plugins.java.api.tree.ArrayDimensionTree;
import org.sonar.plugins.java.api.tree.ExpressionTree;
import org.sonar.plugins.java.api.tree.IdentifierTree;
import org.sonar.plugins.java.api.tree.ImportClauseTree;
import org.sonar.plugins.java.api.tree.ListTree;
import org.sonar.plugins.java.api.tree.ModifierTree;
import org.sonar.plugins.java.api.tree.PackageDeclarationTree;
import org.sonar.plugins.java.api.tree.ParameterizedTypeTree;
import org.sonar.plugins.java.api.tree.StatementTree;
import org.sonar.plugins.java.api.tree.SyntaxToken;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.Tree.Kind;
import org.sonar.plugins.java.api.tree.TypeArguments;
import org.sonar.plugins.java.api.tree.TypeParameterTree;
import org.sonar.plugins.java.api.tree.TypeTree;
import org.sonar.plugins.java.api.tree.VariableTree;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import java.util.Collections;
import java.util.List;

public class TreeFactory {

  private final KindMaps kindMaps = new KindMaps();

  public ModifiersTreeImpl modifiers(Optional> modifierNodes) {
    if (!modifierNodes.isPresent()) {
      return ModifiersTreeImpl.emptyModifiers();
    }
    return new ModifiersTreeImpl(modifierNodes.get());
  }

  public ModifierKeywordTreeImpl modifierKeyword(InternalSyntaxToken token) {
    JavaKeyword keyword = (JavaKeyword) token.getGrammarRuleKey();
    return new ModifierKeywordTreeImpl(kindMaps.getModifier(keyword), token);
  }

  // Literals

  public ExpressionTree literal(InternalSyntaxToken token) {
    return new LiteralTreeImpl(kindMaps.getLiteral(token.getGrammarRuleKey()), token);
  }

  // End of literals

  // Compilation unit

  public CompilationUnitTreeImpl newCompilationUnit(
    JavaTree spacing,
    Optional packageDeclaration,
    Optional> importDeclarations,
    Optional> typeDeclarations,
    InternalSyntaxToken eof) {

    ImmutableList.Builder imports = ImmutableList.builder();
    if (importDeclarations.isPresent()) {
      for (ImportClauseTree child : importDeclarations.get()) {
        imports.add(child);
      }
    }

    ImmutableList.Builder types = ImmutableList.builder();
    if (typeDeclarations.isPresent()) {
      for (Tree child : typeDeclarations.get()) {
        types.add(child);
      }
    }

    return new CompilationUnitTreeImpl(
      packageDeclaration.orNull(),
      imports.build(),
      types.build(),
      eof);
  }

  public PackageDeclarationTreeImpl newPackageDeclaration(Optional> annotations, InternalSyntaxToken packageToken, ExpressionTree qualifiedIdentifier,
    InternalSyntaxToken semicolonToken) {
    List annotationList = Collections.emptyList();
    if (annotations.isPresent()) {
      annotationList = ImmutableList.builder().addAll(annotations.get()).build();
    }
    return new PackageDeclarationTreeImpl(annotationList, packageToken, qualifiedIdentifier, semicolonToken);
  }

  public ImportClauseTree newEmptyImport(InternalSyntaxToken semicolonToken) {
    return new EmptyStatementTreeImpl(semicolonToken);
  }

  public ImportTreeImpl newImportDeclaration(InternalSyntaxToken importToken, Optional staticToken, ExpressionTree qualifiedIdentifier,
    Optional> dotStar,
    InternalSyntaxToken semicolonToken) {

    ExpressionTree target = qualifiedIdentifier;
    if (dotStar.isPresent()) {
      IdentifierTreeImpl identifier = new IdentifierTreeImpl(dotStar.get().second());
      InternalSyntaxToken dotToken = dotStar.get().first();
      target = new MemberSelectExpressionTreeImpl(qualifiedIdentifier, dotToken, identifier);
    }

    InternalSyntaxToken staticKeyword = staticToken.orNull();
    return new ImportTreeImpl(importToken, staticKeyword, target, semicolonToken);
  }

  public ClassTreeImpl newTypeDeclaration(ModifiersTreeImpl modifiers, ClassTreeImpl partial) {
    return partial.completeModifiers(modifiers);
  }

  public Tree newEmptyType(InternalSyntaxToken semicolonToken) {
    return new EmptyStatementTreeImpl(semicolonToken);
  }

  // End of compilation unit

  // Types

  public TypeTree newType(TypeTree basicOrClassType,
    Optional>, Tuple>>> dims) {
    if (!dims.isPresent()) {
      return basicOrClassType;
    } else {
      TypeTree result = basicOrClassType;

      for (Tuple>, Tuple> dim : dims.get()) {
        result = newArrayTypeTreeWithAnnotations(result, dim);
      }
      return result;
    }
  }

  public TypeArgumentListTreeImpl newTypeArgumentList(InternalSyntaxToken openBracketToken,
    Tree typeArgument, Optional>> rests, InternalSyntaxToken closeBracketToken) {
    ImmutableList.Builder typeArguments = ImmutableList.builder();
    ImmutableList.Builder separators = ImmutableList.builder();

    typeArguments.add(typeArgument);

    if (rests.isPresent()) {
      for (Tuple rest : rests.get()) {
        separators.add(rest.first());
        typeArguments.add(rest.second());
      }
    }
    return new TypeArgumentListTreeImpl(openBracketToken, typeArguments.build(), separators.build(), closeBracketToken);
  }

  public TypeArgumentListTreeImpl newDiamondTypeArgument(InternalSyntaxToken openBracketToken, InternalSyntaxToken closeBracketToken) {
    return new TypeArgumentListTreeImpl(openBracketToken, ImmutableList.of(), ImmutableList.of(), closeBracketToken);
  }

  public Tree completeTypeArgument(Optional> annotations, Tree partial) {
    if (partial.is(Tree.Kind.UNBOUNDED_WILDCARD, Tree.Kind.EXTENDS_WILDCARD, Tree.Kind.SUPER_WILDCARD)) {
      List annotationList = annotations.isPresent() ?
        ImmutableList.builder().addAll(annotations.get()).build() :
        ImmutableList.of();
      ((WildcardTreeImpl) partial).complete(annotationList);
    } else {
      completeTypeTreeWithAnnotations((TypeTree) partial, annotations);
    }
    return partial;
  }

  public TypeTree newBasicTypeArgument(TypeTree type) {
    return type;
  }

  public WildcardTreeImpl completeWildcardTypeArgument(InternalSyntaxToken queryToken, Optional partial) {
    return partial.isPresent() ?
      partial.get().complete(queryToken) :
      new WildcardTreeImpl(queryToken);
  }

  public WildcardTreeImpl newWildcardTypeArguments(InternalSyntaxToken extendsOrSuperToken, Optional> annotations, TypeTree type) {

    completeTypeTreeWithAnnotations(type, annotations);

    return new WildcardTreeImpl(
      JavaKeyword.EXTENDS.getValue().equals(extendsOrSuperToken.text()) ? Kind.EXTENDS_WILDCARD : Kind.SUPER_WILDCARD,
      extendsOrSuperToken,
      type);
  }

  public TypeParameterListTreeImpl newTypeParameterList(InternalSyntaxToken openBracketToken, TypeParameterTreeImpl typeParameter, Optional>> rests, InternalSyntaxToken closeBracketToken) {
    ImmutableList.Builder typeParameters = ImmutableList.builder();
    typeParameters.add(typeParameter);

    ImmutableList.Builder separators = ImmutableList.builder();
    if (rests.isPresent()) {
      for (Tuple rest : rests.get()) {
        separators.add(rest.first());
        typeParameters.add(rest.second());
      }
    }

    return new TypeParameterListTreeImpl(openBracketToken, typeParameters.build(), separators.build(), closeBracketToken);
  }

  public TypeParameterTreeImpl completeTypeParameter(Optional> annotations, InternalSyntaxToken identifierToken, Optional partial) {
    IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);
    completeTypeTreeWithAnnotations(identifier, annotations);
    return partial.isPresent() ?
      partial.get().complete(identifier) :
      new TypeParameterTreeImpl(identifier);
  }

  public TypeParameterTreeImpl newTypeParameter(InternalSyntaxToken extendsToken, BoundListTreeImpl bounds) {
    return new TypeParameterTreeImpl(extendsToken, bounds);
  }

  public BoundListTreeImpl newBounds(TypeTree classType, Optional>> rests) {
    ImmutableList.Builder classTypes = ImmutableList.builder();
    ImmutableList.Builder separators = ImmutableList.builder();

    classTypes.add(classType);
    if (rests.isPresent()) {
      for (Tuple rest : rests.get()) {
        separators.add(rest.first());
        classTypes.add(rest.second());
      }
    }
    return new BoundListTreeImpl(classTypes.build(), separators.build());
  }

  // End of types

  // Classes, enums and interfaces

  public ClassTreeImpl completeClassDeclaration(
    InternalSyntaxToken classSyntaxToken,
    InternalSyntaxToken identifierToken, Optional typeParameters,
    Optional> extendsClause,
    Optional> implementsClause,
    ClassTreeImpl partial) {

    IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);

    partial.completeDeclarationKeyword(classSyntaxToken);
    partial.completeIdentifier(identifier);
    if (typeParameters.isPresent()) {
      partial.completeTypeParameters(typeParameters.get());
    }
    if (extendsClause.isPresent()) {
      partial.completeSuperclass(extendsClause.get().first(), extendsClause.get().second());
    }
    if (implementsClause.isPresent()) {
      InternalSyntaxToken implementsKeyword = implementsClause.get().first();
      QualifiedIdentifierListTreeImpl interfaces = implementsClause.get().second();
      partial.completeInterfaces(implementsKeyword, interfaces);
    }

    return partial;
  }

  private static ClassTreeImpl newClassBody(Kind kind, InternalSyntaxToken openBraceSyntaxToken,
    Optional> members, InternalSyntaxToken closeBraceTokenSyntaxToken) {
    ImmutableList.Builder builder = ImmutableList.builder();
    if (members.isPresent()) {
      for (JavaTree member : members.get()) {
        if (member instanceof VariableDeclaratorListTreeImpl) {
          for (VariableTreeImpl variable : (VariableDeclaratorListTreeImpl) member) {
            builder.add(variable);
          }
        } else {
          builder.add(member);
        }
      }
    }

    return new ClassTreeImpl(kind, openBraceSyntaxToken, builder.build(), closeBraceTokenSyntaxToken);
  }

  public ClassTreeImpl newClassBody(InternalSyntaxToken openBraceToken, Optional> members, InternalSyntaxToken closeBraceToken) {
    return newClassBody(Kind.CLASS, openBraceToken, members, closeBraceToken);
  }

  public ClassTreeImpl newEnumDeclaration(
    InternalSyntaxToken enumToken,
    InternalSyntaxToken identifierToken,
    Optional> implementsClause,
    InternalSyntaxToken openBraceToken,
    Optional> enumConstants,
    Optional semicolonToken,
    Optional> enumDeclarations,
    InternalSyntaxToken closeBraceToken) {

    List members = Lists.newLinkedList();
    EnumConstantTreeImpl lastEnumConstant = null;
    if (enumConstants.isPresent()) {
      for (EnumConstantTreeImpl enumConstant : enumConstants.get()) {
        members.add(enumConstant);
        lastEnumConstant = enumConstant;
      }
    }
    if (semicolonToken.isPresent()) {
      InternalSyntaxToken semicolon = semicolonToken.get();
      // add the semicolon as endToken of the last enumConstant, or as empty statement in the enum members
      if (lastEnumConstant != null) {
        lastEnumConstant.setEndToken(semicolon);
      } else {
        members.add(newEmptyMember(semicolon));
      }
    }
    if (enumDeclarations.isPresent()) {
      for (JavaTree enumDeclaration : enumDeclarations.get()) {
        members.add(enumDeclaration);
      }
    }

    ClassTreeImpl result = newClassBody(Kind.ENUM, openBraceToken, Optional.of((List) ImmutableList.builder().addAll(members).build()), closeBraceToken);

    result.completeDeclarationKeyword(enumToken);

    IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);
    result.completeIdentifier(identifier);

    if (implementsClause.isPresent()) {
      InternalSyntaxToken implementsKeyword = implementsClause.get().first();
      QualifiedIdentifierListTreeImpl interfaces = implementsClause.get().second();
      result.completeInterfaces(implementsKeyword, interfaces);
    }

    return result;
  }

  public EnumConstantTreeImpl newEnumConstant(
    Optional> annotations, InternalSyntaxToken identifierToken,
    Optional arguments,
    Optional classBody,
    Optional commaToken) {

    IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);

    ArgumentListTreeImpl defaultArguments = new ArgumentListTreeImpl(ImmutableList.of(), ImmutableList.of());
    NewClassTreeImpl newClass = new NewClassTreeImpl(arguments.or(defaultArguments), classBody.orNull());
    newClass.completeWithIdentifier(identifier);

    return new EnumConstantTreeImpl(modifiers((Optional>) (Optional) annotations), identifier, newClass, commaToken.orNull());
  }

  public ClassTreeImpl completeInterfaceDeclaration(
    InternalSyntaxToken interfaceToken,
    InternalSyntaxToken identifierToken, Optional typeParameters,
    Optional> extendsClause,
    ClassTreeImpl partial) {

    IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);

    partial.completeDeclarationKeyword(interfaceToken);

    partial.completeIdentifier(identifier);
    if (typeParameters.isPresent()) {
      partial.completeTypeParameters(typeParameters.get());
    }
    if (extendsClause.isPresent()) {
      InternalSyntaxToken extendsKeyword = extendsClause.get().first();
      QualifiedIdentifierListTreeImpl interfaces = extendsClause.get().second();
      partial.completeInterfaces(extendsKeyword, interfaces);
    }

    return partial;
  }

  public ClassTreeImpl newInterfaceBody(InternalSyntaxToken openBraceToken, Optional> members, InternalSyntaxToken closeBraceToken) {
    return newClassBody(Kind.INTERFACE, openBraceToken, members, closeBraceToken);
  }

  // TODO Create an intermediate implementation interface for completing modifiers
  public JavaTree completeMember(ModifiersTreeImpl modifiers, JavaTree partial) {

    if (partial instanceof ClassTreeImpl) {
      ((ClassTreeImpl) partial).completeModifiers(modifiers);
    } else if (partial instanceof VariableDeclaratorListTreeImpl) {
      for (VariableTreeImpl variable : (VariableDeclaratorListTreeImpl) partial) {
        variable.completeModifiers(modifiers);
      }
    } else if (partial instanceof MethodTreeImpl) {
      ((MethodTreeImpl) partial).completeWithModifiers(modifiers);
    } else {
      throw new IllegalArgumentException();
    }

    return partial;
  }

  public BlockTreeImpl newInitializerMember(Optional staticToken, BlockTreeImpl block) {
    if (staticToken.isPresent()) {
      return new StaticInitializerTreeImpl(staticToken.get(), (InternalSyntaxToken) block.openBraceToken(), block.body(),
        (InternalSyntaxToken) block.closeBraceToken());
    } else {
      return new BlockTreeImpl(Kind.INITIALIZER, (InternalSyntaxToken) block.openBraceToken(), block.body(), (InternalSyntaxToken) block.closeBraceToken());
    }
  }

  public EmptyStatementTreeImpl newEmptyMember(InternalSyntaxToken semicolonToken) {
    return new EmptyStatementTreeImpl(semicolonToken);
  }

  public MethodTreeImpl completeGenericMethodOrConstructorDeclaration(TypeParameterListTreeImpl typeParameters, MethodTreeImpl partial) {
    return partial.completeWithTypeParameters(typeParameters);
  }

  private MethodTreeImpl newMethodOrConstructor(
    Optional type, InternalSyntaxToken identifierToken, FormalParametersListTreeImpl parameters,
    Optional>, Tuple>>> annotatedDimensions,
    Optional> throwsClause,
    JavaTree blockOrSemicolon) {

    IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);

    ArrayTypeTreeImpl nestedDimensions = newArrayTypeTreeWithAnnotations(annotatedDimensions);
    TypeTree actualType;
    if (type.isPresent()) {
      actualType = applyDim(type.get(), nestedDimensions);
    } else {
      actualType = null;
    }
    BlockTreeImpl block = null;
    InternalSyntaxToken semicolonToken = null;
    if (blockOrSemicolon.is(Tree.Kind.BLOCK)) {
      block = (BlockTreeImpl) blockOrSemicolon;
    } else {
      semicolonToken = (InternalSyntaxToken) blockOrSemicolon;
    }

    InternalSyntaxToken throwsToken = null;
    ListTree throwsClauses = QualifiedIdentifierListTreeImpl.emptyList();
    if (throwsClause.isPresent()) {
      throwsToken = throwsClause.get().first();
      throwsClauses = throwsClause.get().second();
    }

    return new MethodTreeImpl(
      actualType,
      identifier,
      parameters,
      throwsToken,
      throwsClauses,
      block,
      semicolonToken);
  }

  public MethodTreeImpl newMethod(
    TypeTree type, InternalSyntaxToken identifierToken, FormalParametersListTreeImpl parameters,
    Optional>, Tuple>>> annotatedDimensions,
    Optional> throwsClause,
    JavaTree blockOrSemicolon) {

    return newMethodOrConstructor(Optional.of(type), identifierToken, parameters, annotatedDimensions, throwsClause, blockOrSemicolon);
  }

  public MethodTreeImpl newConstructor(
    InternalSyntaxToken identifierToken, FormalParametersListTreeImpl parameters,
    Optional>, Tuple>>> annotatedDimensions,
    Optional> throwsClause,
    JavaTree blockOrSemicolon) {

    return newMethodOrConstructor(Optional.absent(), identifierToken, parameters, annotatedDimensions, throwsClause, blockOrSemicolon);
  }

  public VariableDeclaratorListTreeImpl completeFieldDeclaration(TypeTree type, VariableDeclaratorListTreeImpl partial, InternalSyntaxToken semicolonToken) {
    for (VariableTreeImpl variable : partial) {
      variable.completeType(type);
    }

    // store the semicolon as endToken for the last variable
    partial.get(partial.size() - 1).setEndToken(semicolonToken);

    return partial;
  }

  // End of classes, enums and interfaces

  // Annotations

  public ClassTreeImpl completeAnnotationType(InternalSyntaxToken atToken, InternalSyntaxToken interfaceToken, InternalSyntaxToken identifier, ClassTreeImpl partial) {
    return partial.complete(
      atToken,
      interfaceToken,
      new IdentifierTreeImpl(identifier));
  }

  public ClassTreeImpl newAnnotationType(InternalSyntaxToken openBraceToken, Optional> annotationTypeElementDeclarations, InternalSyntaxToken closeBraceToken) {
    // TODO
    ModifiersTreeImpl emptyModifiers = ModifiersTreeImpl.emptyModifiers();

    ImmutableList.Builder members = ImmutableList.builder();

    if (annotationTypeElementDeclarations.isPresent()) {
      for (JavaTree annotationTypeElementDeclaration : annotationTypeElementDeclarations.get()) {
        if (annotationTypeElementDeclaration.getGrammarRuleKey().equals(JavaLexer.VARIABLE_DECLARATORS)) {
          for (VariableTreeImpl variable : (VariableDeclaratorListTreeImpl) annotationTypeElementDeclaration) {
            members.add(variable);
          }
        } else if (!annotationTypeElementDeclaration.is(Kind.TOKEN)) {
          members.add(annotationTypeElementDeclaration);
        }
      }
    }

    return new ClassTreeImpl(emptyModifiers, openBraceToken, members.build(), closeBraceToken);
  }

  public JavaTree completeAnnotationTypeMember(ModifiersTreeImpl modifiers, JavaTree partial) {

    if (partial.getGrammarRuleKey().equals(JavaLexer.VARIABLE_DECLARATORS)) {
      for (VariableTreeImpl variable : (VariableDeclaratorListTreeImpl) partial) {
        variable.completeModifiers(modifiers);
      }
    } else if (partial.is(Kind.CLASS, Kind.INTERFACE, Kind.ENUM, Kind.ANNOTATION_TYPE)) {
      ((ClassTreeImpl) partial).completeModifiers(modifiers);
    } else if (partial.is(Kind.METHOD)) {
      ((MethodTreeImpl) partial).completeWithModifiers(modifiers);
    } else {
      throw new IllegalArgumentException("Unsupported type: " + partial);
    }

    return partial;
  }

  public MethodTreeImpl completeAnnotationMethod(TypeTree type, InternalSyntaxToken identifierToken, MethodTreeImpl partial, InternalSyntaxToken semiToken) {
    partial.complete(type, new IdentifierTreeImpl(identifierToken), semiToken);
    return partial;
  }

  public MethodTreeImpl newAnnotationTypeMethod(InternalSyntaxToken openParenToken, InternalSyntaxToken closeParenToken,
    Optional> defaultValue) {
    FormalParametersListTreeImpl parameters = new FormalParametersListTreeImpl(openParenToken, closeParenToken);
    InternalSyntaxToken defaultToken = null;
    ExpressionTree defaultExpression = null;
    if (defaultValue.isPresent()) {
      defaultToken = defaultValue.get().first();
      defaultExpression = defaultValue.get().second();
    }
    return new MethodTreeImpl(parameters, defaultToken, defaultExpression);
  }

  public Tuple newDefaultValue(InternalSyntaxToken defaultToken, ExpressionTree elementValue) {
    return new Tuple<>(defaultToken, elementValue);
  }

  public AnnotationTreeImpl newAnnotation(InternalSyntaxToken atToken, TypeTree qualifiedIdentifier, Optional arguments) {
    ArgumentListTreeImpl defaultValue = new ArgumentListTreeImpl(ImmutableList.of(), ImmutableList.of());
    return new AnnotationTreeImpl(atToken, qualifiedIdentifier, arguments.or(defaultValue));
  }

  public ArgumentListTreeImpl completeNormalAnnotation(InternalSyntaxToken openParenToken, Optional partial, InternalSyntaxToken closeParenToken) {
    if (!partial.isPresent()) {
      return new ArgumentListTreeImpl(openParenToken, closeParenToken);
    }

    ArgumentListTreeImpl elementValuePairs = partial.get();
    elementValuePairs.complete(openParenToken, closeParenToken);

    return elementValuePairs;
  }

  public ArgumentListTreeImpl newNormalAnnotation(AssignmentExpressionTreeImpl elementValuePair, Optional>> rests) {
    ImmutableList.Builder expressions = ImmutableList.builder();
    expressions.add(elementValuePair);

    ImmutableList.Builder separators = ImmutableList.builder();
    if (rests.isPresent()) {
      for (Tuple rest : rests.get()) {
        separators.add(rest.first());
        expressions.add(rest.second());
      }
    }

    return new ArgumentListTreeImpl(expressions.build(), separators.build());
  }

  public AssignmentExpressionTreeImpl newElementValuePair(InternalSyntaxToken identifierToken, InternalSyntaxToken operator, ExpressionTree elementValue) {
    return new AssignmentExpressionTreeImpl(
      kindMaps.getAssignmentOperator((JavaPunctuator) operator.getGrammarRuleKey()),
      new IdentifierTreeImpl(identifierToken),
      operator,
      elementValue);
  }

  public NewArrayTreeImpl completeElementValueArrayInitializer(
    InternalSyntaxToken openBraceToken, Optional partial, InternalSyntaxToken closeBraceToken) {

    NewArrayTreeImpl elementValues = partial.or(new NewArrayTreeImpl(ImmutableList.of(), InitializerListTreeImpl.emptyList()));

    return elementValues.completeWithCurlyBraces(openBraceToken, closeBraceToken);
  }

  public NewArrayTreeImpl newElementValueArrayInitializer(List>> rests) {
    ImmutableList.Builder expressions = ImmutableList.builder();
    ImmutableList.Builder separators = ImmutableList.builder();
    for (Tuple> tuple : rests) {
      expressions.add(tuple.first());
      if (tuple.second().isPresent()) {
        separators.add(tuple.second().get());
      }
    }
    return new NewArrayTreeImpl(ImmutableList.of(), new InitializerListTreeImpl(expressions.build(), separators.build()));
  }

  public ArgumentListTreeImpl newSingleElementAnnotation(InternalSyntaxToken openParenToken, ExpressionTree elementValue, InternalSyntaxToken closeParenToken) {
    return new ArgumentListTreeImpl(openParenToken, elementValue, closeParenToken);
  }

  // End of annotations

  // Formal parameters

  public FormalParametersListTreeImpl completeParenFormalParameters(InternalSyntaxToken openParenToken, Optional partial,
    InternalSyntaxToken closeParenToken) {

    return partial.isPresent() ?
      partial.get().complete(openParenToken, closeParenToken) :
      new FormalParametersListTreeImpl(openParenToken, closeParenToken);
  }

  public FormalParametersListTreeImpl completeTypeFormalParameters(ModifiersTreeImpl modifiers, TypeTree type, FormalParametersListTreeImpl partial) {
    VariableTreeImpl variable = partial.get(0);

    variable.completeModifiersAndType(modifiers, type);

    return partial;
  }

  public FormalParametersListTreeImpl prependNewFormalParameter(VariableTreeImpl variable, Optional> rest) {
    if (rest.isPresent()) {
      InternalSyntaxToken comma = rest.get().first();
      FormalParametersListTreeImpl partial = rest.get().second();

      partial.add(0, variable);

      // store the comma as endToken for the variable
      variable.setEndToken(comma);

      return partial;
    } else {
      return new FormalParametersListTreeImpl(variable);
    }
  }

  public FormalParametersListTreeImpl newVariableArgumentFormalParameter(Optional> annotations,
    InternalSyntaxToken ellipsisToken, VariableTreeImpl variable) {
    variable.addEllipsisDimension(new ArrayTypeTreeImpl(null, annotations.or(ImmutableList.of()), ellipsisToken));

    return new FormalParametersListTreeImpl(
      annotations.or(ImmutableList.of()),
      ellipsisToken,
      variable);
  }

  public VariableTreeImpl newVariableDeclaratorId(InternalSyntaxToken identifierToken,
    Optional>, Tuple>>> dims) {
    IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);
    ArrayTypeTreeImpl nestedDimensions = newArrayTypeTreeWithAnnotations(dims);
    return new VariableTreeImpl(identifier, nestedDimensions);
  }

  public VariableTreeImpl newFormalParameter(ModifiersTreeImpl modifiers, TypeTree type, VariableTreeImpl variable) {
    return variable.completeType(type);
  }

  // End of formal parameters

  // Statements

  public VariableDeclaratorListTreeImpl completeLocalVariableDeclaration(
    ModifiersTreeImpl modifiers,
    TypeTree type,
    VariableDeclaratorListTreeImpl variables,
    InternalSyntaxToken semicolonSyntaxToken) {

    for (VariableTreeImpl variable : variables) {
      variable.completeModifiersAndType(modifiers, type);
    }

    // store the semicolon as endToken for the last variable
    variables.get(variables.size() - 1).setEndToken(semicolonSyntaxToken);

    return variables;
  }

  public VariableDeclaratorListTreeImpl newVariableDeclarators(VariableTreeImpl variable, Optional>> rests) {
    ImmutableList.Builder variables = ImmutableList.builder();

    variables.add(variable);
    if (rests.isPresent()) {
      VariableTreeImpl previousVariable = variable;
      for (Tuple rest : rests.get()) {
        VariableTreeImpl newVariable = rest.second();
        InternalSyntaxToken separator = rest.first();

        variables.add(newVariable);

        // store the separator
        previousVariable.setEndToken(separator);
        previousVariable = newVariable;
      }
    }

    return new VariableDeclaratorListTreeImpl(variables.build());
  }

  public VariableTreeImpl completeVariableDeclarator(InternalSyntaxToken identifierToken,
    Optional>, Tuple>>> dimensions,
    Optional partial) {
    IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);

    ArrayTypeTreeImpl nestedDimensions = newArrayTypeTreeWithAnnotations(dimensions);

    if (partial.isPresent()) {
      return partial.get().completeIdentifierAndDims(identifier, nestedDimensions);
    } else {
      return new VariableTreeImpl(identifier, nestedDimensions);
    }
  }

  public VariableTreeImpl newVariableDeclarator(InternalSyntaxToken equalToken, ExpressionTree initializer) {
    return new VariableTreeImpl(equalToken, initializer);
  }

  public BlockTreeImpl block(InternalSyntaxToken openBraceToken, BlockStatementListTreeImpl blockStatements, InternalSyntaxToken closeBraceToken) {
    return new BlockTreeImpl(openBraceToken, blockStatements, closeBraceToken);
  }

  public AssertStatementTreeImpl completeAssertStatement(
    InternalSyntaxToken assertToken, ExpressionTree expression, Optional detailExpression, InternalSyntaxToken semicolonSyntaxToken) {

    return detailExpression.isPresent() ?
      detailExpression.get().complete(assertToken, expression, semicolonSyntaxToken) :
      new AssertStatementTreeImpl(assertToken, expression, semicolonSyntaxToken);
  }

  public AssertStatementTreeImpl newAssertStatement(InternalSyntaxToken colonToken, ExpressionTree expression) {
    return new AssertStatementTreeImpl(colonToken, expression);
  }

  public IfStatementTreeImpl completeIf(InternalSyntaxToken ifToken, InternalSyntaxToken openParenToken, ExpressionTree condition, InternalSyntaxToken closeParenToken,
    StatementTree statement,
    Optional elseClause) {
    if (elseClause.isPresent()) {
      return elseClause.get().complete(ifToken, openParenToken, condition, closeParenToken, statement);
    } else {
      return new IfStatementTreeImpl(ifToken, openParenToken, condition, closeParenToken, statement);
    }
  }

  public IfStatementTreeImpl newIfWithElse(InternalSyntaxToken elseToken, StatementTree elseStatement) {
    return new IfStatementTreeImpl(elseToken, elseStatement);
  }

  public ForStatementTreeImpl newStandardForStatement(
    InternalSyntaxToken forTokenKeyword,
    InternalSyntaxToken openParenToken,
    Optional forInit, InternalSyntaxToken forInitSemicolonToken,
    Optional expression, InternalSyntaxToken expressionSemicolonToken,
    Optional forUpdate, InternalSyntaxToken closeParenToken,
    StatementTree statement) {

    StatementExpressionListTreeImpl forInitStatement = forInit.or(new StatementExpressionListTreeImpl(ImmutableList.of(), ImmutableList.of()));
    StatementExpressionListTreeImpl forUpdateStatement = forUpdate.or(new StatementExpressionListTreeImpl(ImmutableList.of(), ImmutableList.of()));

    return new ForStatementTreeImpl(
      forTokenKeyword,
      openParenToken,
      forInitStatement,
      forInitSemicolonToken,
      expression.orNull(),
      expressionSemicolonToken,
      forUpdateStatement,
      closeParenToken,
      statement);
  }

  public StatementExpressionListTreeImpl newForInitDeclaration(ModifiersTreeImpl modifiers, TypeTree type, VariableDeclaratorListTreeImpl variables) {
    for (VariableTreeImpl variable : variables) {
      variable.completeModifiersAndType(modifiers, type);
    }
    return new StatementExpressionListTreeImpl(variables, ImmutableList.of());
  }

  public StatementExpressionListTreeImpl newStatementExpressions(ExpressionTree expression, Optional>> rests) {
    ImmutableList.Builder statements = ImmutableList.builder();
    statements.add(new ExpressionStatementTreeImpl(expression, null));
    ImmutableList.Builder separators = ImmutableList.builder();
    if (rests.isPresent()) {
      for (Tuple rest : rests.get()) {
        separators.add(rest.first());
        statements.add(new ExpressionStatementTreeImpl(rest.second(), null));
      }
    }
    return new StatementExpressionListTreeImpl(statements.build(), separators.build());
  }

  public ForEachStatementImpl newForeachStatement(
    InternalSyntaxToken forKeyword,
    InternalSyntaxToken openParenToken,
    VariableTreeImpl variable, InternalSyntaxToken colonToken, ExpressionTree expression,
    InternalSyntaxToken closeParenToken,
    StatementTree statement) {
    return new ForEachStatementImpl(forKeyword, openParenToken, variable, colonToken, expression, closeParenToken, statement);
  }

  public WhileStatementTreeImpl whileStatement(InternalSyntaxToken whileToken, InternalSyntaxToken openParen, ExpressionTree expression, InternalSyntaxToken closeParen,
    StatementTree statement) {
    return new WhileStatementTreeImpl(whileToken, openParen, expression, closeParen, statement);
  }

  public DoWhileStatementTreeImpl doWhileStatement(InternalSyntaxToken doToken, StatementTree statement,
    InternalSyntaxToken whileToken, InternalSyntaxToken openParen, ExpressionTree expression,
    InternalSyntaxToken closeParen, InternalSyntaxToken semicolon) {
    return new DoWhileStatementTreeImpl(doToken, statement, whileToken, openParen, expression, closeParen, semicolon);
  }

  public TryStatementTreeImpl completeStandardTryStatement(InternalSyntaxToken tryToken, BlockTreeImpl block, TryStatementTreeImpl partial) {
    return partial.completeStandardTry(tryToken, block);
  }

  public TryStatementTreeImpl newTryCatch(Optional> catches, Optional finallyBlock) {
    List catchTrees = catches.or(ImmutableList.of());
    if (finallyBlock.isPresent()) {
      return finallyBlock.get().completeWithCatches(catchTrees);
    } else {
      return new TryStatementTreeImpl(catchTrees, null, null);
    }
  }

  public CatchTreeImpl newCatchClause(InternalSyntaxToken catchToken, InternalSyntaxToken openParenToken, VariableTreeImpl parameter,
    InternalSyntaxToken closeParenToken, BlockTreeImpl block) {
    return new CatchTreeImpl(catchToken, openParenToken, parameter, closeParenToken, block);
  }

  public VariableTreeImpl newCatchFormalParameter(ModifiersTreeImpl modifiers, TypeTree type, VariableTreeImpl parameter) {
    if (!modifiers.isEmpty()) {
      parameter.completeModifiers(modifiers);
    }
    return parameter.completeType(type);
  }

  public TypeTree newCatchType(TypeTree qualifiedIdentifier, Optional>> rests) {
    if (!rests.isPresent()) {
      return qualifiedIdentifier;
    }
    ImmutableList.Builder types = ImmutableList.builder();
    types.add(qualifiedIdentifier);
    ImmutableList.Builder separators = ImmutableList.builder();
    for (Tuple rest : rests.get()) {
      separators.add(rest.first());
      types.add(rest.second());
    }
    return new UnionTypeTreeImpl(new TypeUnionListTreeImpl(types.build(), separators.build()));
  }

  public TryStatementTreeImpl newFinallyBlock(InternalSyntaxToken finallyToken, BlockTreeImpl block) {
    return new TryStatementTreeImpl(finallyToken, block);
  }

  public TryStatementTreeImpl newTryWithResourcesStatement(
    InternalSyntaxToken tryToken, InternalSyntaxToken openParenToken, ResourceListTreeImpl resources, InternalSyntaxToken closeParenToken,
    BlockTreeImpl block,
    Optional> catches, Optional finallyBlock) {

    List catchTrees = catches.or(ImmutableList.of());
    if (finallyBlock.isPresent()) {
      return finallyBlock.get().completeTryWithResources(tryToken, openParenToken, resources, closeParenToken, block, catchTrees);
    } else {
      return new TryStatementTreeImpl(tryToken, openParenToken, resources, closeParenToken, block, catchTrees);
    }
  }

  public ResourceListTreeImpl newResources(List>> rests) {
    ImmutableList.Builder resources = ImmutableList.builder();
    ImmutableList.Builder separators = ImmutableList.builder();

    for (Tuple> rest : rests) {
      if (rest.second().isPresent()) {
        separators.add(rest.second().get());
      }
      resources.add(rest.first());
    }

    return new ResourceListTreeImpl(resources.build(), separators.build());
  }

  public VariableTreeImpl newResource(ModifiersTreeImpl modifiers, TypeTree classType, VariableTreeImpl partial, InternalSyntaxToken equalToken, ExpressionTree expression) {
    if (!modifiers.isEmpty()) {
      partial.completeModifiers(modifiers);
    }
    return partial.completeTypeAndInitializer(classType, equalToken, expression);
  }

  public SwitchStatementTreeImpl switchStatement(InternalSyntaxToken switchToken, InternalSyntaxToken openParenToken, ExpressionTree expression,
    InternalSyntaxToken closeParenToken,
    InternalSyntaxToken openBraceToken, Optional> optionalGroups, InternalSyntaxToken closeBraceToken) {

    List groups = optionalGroups.or(Collections.emptyList());

    return new SwitchStatementTreeImpl(switchToken, openParenToken, expression, closeParenToken,
      openBraceToken, groups, closeBraceToken);
  }

  public CaseGroupTreeImpl switchGroup(List labels, BlockStatementListTreeImpl blockStatements) {
    return new CaseGroupTreeImpl(labels, blockStatements);
  }

  public CaseLabelTreeImpl newCaseSwitchLabel(InternalSyntaxToken caseSyntaxToken, ExpressionTree expression, InternalSyntaxToken colonSyntaxToken) {
    return new CaseLabelTreeImpl(caseSyntaxToken, expression, colonSyntaxToken);
  }

  public CaseLabelTreeImpl newDefaultSwitchLabel(InternalSyntaxToken defaultToken, InternalSyntaxToken colonToken) {
    return new CaseLabelTreeImpl(defaultToken, null, colonToken);
  }

  public SynchronizedStatementTreeImpl synchronizedStatement(InternalSyntaxToken synchronizedToken, InternalSyntaxToken openParenToken, ExpressionTree expression,
    InternalSyntaxToken closeParenToken, BlockTreeImpl block) {
    return new SynchronizedStatementTreeImpl(synchronizedToken, openParenToken, expression, closeParenToken, block);
  }

  public BreakStatementTreeImpl breakStatement(InternalSyntaxToken breakToken, Optional identifierToken, InternalSyntaxToken semicolonSyntaxToken) {
    IdentifierTreeImpl identifier = null;
    if (identifierToken.isPresent()) {
      identifier = new IdentifierTreeImpl(identifierToken.get());
    }
    return new BreakStatementTreeImpl(breakToken, identifier, semicolonSyntaxToken);
  }

  public ContinueStatementTreeImpl continueStatement(InternalSyntaxToken continueToken, Optional identifierToken, InternalSyntaxToken semicolonToken) {
    IdentifierTreeImpl identifier = null;
    if (identifierToken.isPresent()) {
      identifier = new IdentifierTreeImpl(identifierToken.get());
    }
    return new ContinueStatementTreeImpl(continueToken, identifier, semicolonToken);
  }

  public ReturnStatementTreeImpl returnStatement(InternalSyntaxToken returnToken, Optional expression, InternalSyntaxToken semicolonSyntaxToken) {
    return new ReturnStatementTreeImpl(returnToken, expression.orNull(), semicolonSyntaxToken);
  }

  public ThrowStatementTreeImpl throwStatement(InternalSyntaxToken throwToken, ExpressionTree expression, InternalSyntaxToken semicolonToken) {
    return new ThrowStatementTreeImpl(throwToken, expression, semicolonToken);
  }

  public LabeledStatementTreeImpl labeledStatement(InternalSyntaxToken identifierToken, InternalSyntaxToken colon, StatementTree statement) {
    IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);
    return new LabeledStatementTreeImpl(identifier, colon, statement);
  }

  public ExpressionStatementTreeImpl expressionStatement(ExpressionTree expression, InternalSyntaxToken semicolonToken) {
    return new ExpressionStatementTreeImpl(expression, semicolonToken);
  }

  public EmptyStatementTreeImpl emptyStatement(InternalSyntaxToken semicolon) {
    return new EmptyStatementTreeImpl(semicolon);
  }

  public BlockStatementListTreeImpl blockStatements(Optional> blockStatements) {
    ImmutableList.Builder builder = ImmutableList.builder();

    if (blockStatements.isPresent()) {
      for (BlockStatementListTreeImpl blockStatement : blockStatements.get()) {
        builder.addAll(blockStatement);
      }
    }

    return new BlockStatementListTreeImpl(builder.build());
  }

  public BlockStatementListTreeImpl wrapInBlockStatements(VariableDeclaratorListTreeImpl variables) {
    return new BlockStatementListTreeImpl(variables);
  }

  public BlockStatementListTreeImpl newInnerClassOrEnum(ModifiersTreeImpl modifiers, ClassTreeImpl classTree) {
    classTree.completeModifiers(modifiers);
    return new BlockStatementListTreeImpl(ImmutableList.of(classTree));
  }

  public BlockStatementListTreeImpl wrapInBlockStatements(StatementTree statement) {
    return new BlockStatementListTreeImpl(ImmutableList.of(statement));
  }

  // End of statements

  // Expressions

  public ExpressionTree assignmentExpression(ExpressionTree expression, Optional> operatorAndOperands) {
    if (!operatorAndOperands.isPresent()) {
      return expression;
    }

    ExpressionTree result = null;
    InternalSyntaxToken lastOperator = null;
    for (OperatorAndOperand operatorAndOperand : Lists.reverse(operatorAndOperands.get())) {
      if (lastOperator == null) {
        result = operatorAndOperand.operand();
      } else {
        result = new AssignmentExpressionTreeImpl(
          kindMaps.getAssignmentOperator((JavaPunctuator) lastOperator.getGrammarRuleKey()),
          operatorAndOperand.operand(),
          lastOperator,
          result);
      }

      lastOperator = operatorAndOperand.operator();
    }

    result = new AssignmentExpressionTreeImpl(
      kindMaps.getAssignmentOperator((JavaPunctuator) lastOperator.getGrammarRuleKey()),
      expression,
      lastOperator,
      result);

    return result;
  }

  public ExpressionTree completeTernaryExpression(ExpressionTree expression, Optional partial) {
    return partial.isPresent() ?
      partial.get().complete(expression) :
      expression;
  }

  public ConditionalExpressionTreeImpl newTernaryExpression(InternalSyntaxToken queryToken, ExpressionTree trueExpression, InternalSyntaxToken colonToken,
    ExpressionTree falseExpression) {
    return new ConditionalExpressionTreeImpl(queryToken, trueExpression, colonToken, falseExpression);
  }

  public ExpressionTree completeInstanceofExpression(ExpressionTree expression, Optional partial) {
    return partial.isPresent() ?
      partial.get().complete(expression) :
      expression;
  }

  public InstanceOfTreeImpl newInstanceofExpression(InternalSyntaxToken instanceofToken, TypeTree type) {
    return new InstanceOfTreeImpl(instanceofToken, type);
  }

  private static class OperatorAndOperand {

    private final InternalSyntaxToken operator;
    private final ExpressionTree operand;

    public OperatorAndOperand(InternalSyntaxToken operator, ExpressionTree operand) {
      this.operator = operator;
      this.operand = operand;
    }

    public InternalSyntaxToken operator() {
      return operator;
    }

    public ExpressionTree operand() {
      return operand;
    }

  }

  private ExpressionTree binaryExpression(ExpressionTree expression, Optional> operatorAndOperands) {
    if (!operatorAndOperands.isPresent()) {
      return expression;
    }

    ExpressionTree result = expression;
    for (OperatorAndOperand operatorAndOperand : operatorAndOperands.get()) {
      result = new BinaryExpressionTreeImpl(
        kindMaps.getBinaryOperator((JavaPunctuator) operatorAndOperand.operator().getGrammarRuleKey()),
        result,
        operatorAndOperand.operator(),
        operatorAndOperand.operand());
    }
    return result;
  }

  private OperatorAndOperand newOperatorAndOperand(InternalSyntaxToken operator, ExpressionTree operand) {
    return new OperatorAndOperand(operator, operand);
  }

  // TODO Allow to use the same method several times

  public OperatorAndOperand newOperatorAndOperand11(InternalSyntaxToken operator, ExpressionTree operand) {
    return newOperatorAndOperand(operator, operand);
  }

  public ExpressionTree binaryExpression10(ExpressionTree expression, Optional> operatorAndOperands) {
    return binaryExpression(expression, operatorAndOperands);
  }

  public OperatorAndOperand newOperatorAndOperand10(InternalSyntaxToken operator, ExpressionTree operand) {
    return newOperatorAndOperand(operator, operand);
  }

  public ExpressionTree binaryExpression9(ExpressionTree expression, Optional> operatorAndOperands) {
    return binaryExpression(expression, operatorAndOperands);
  }

  public OperatorAndOperand newOperatorAndOperand9(InternalSyntaxToken operator, ExpressionTree operand) {
    return newOperatorAndOperand(operator, operand);
  }

  public ExpressionTree binaryExpression8(ExpressionTree expression, Optional> operatorAndOperands) {
    return binaryExpression(expression, operatorAndOperands);
  }

  public OperatorAndOperand newOperatorAndOperand8(InternalSyntaxToken operator, ExpressionTree operand) {
    return newOperatorAndOperand(operator, operand);
  }

  public ExpressionTree binaryExpression7(ExpressionTree expression, Optional> operatorAndOperands) {
    return binaryExpression(expression, operatorAndOperands);
  }

  public OperatorAndOperand newOperatorAndOperand7(InternalSyntaxToken operator, ExpressionTree operand) {
    return newOperatorAndOperand(operator, operand);
  }

  public ExpressionTree binaryExpression6(ExpressionTree expression, Optional> operatorAndOperands) {
    return binaryExpression(expression, operatorAndOperands);
  }

  public OperatorAndOperand newOperatorAndOperand6(InternalSyntaxToken operator, ExpressionTree operand) {
    return newOperatorAndOperand(operator, operand);
  }

  public ExpressionTree binaryExpression5(ExpressionTree expression, Optional> operatorAndOperands) {
    return binaryExpression(expression, operatorAndOperands);
  }

  public OperatorAndOperand newOperatorAndOperand5(InternalSyntaxToken operator, ExpressionTree operand) {
    return newOperatorAndOperand(operator, operand);
  }

  public ExpressionTree binaryExpression4(ExpressionTree expression, Optional> operatorAndOperands) {
    return binaryExpression(expression, operatorAndOperands);
  }

  public OperatorAndOperand newOperatorAndOperand4(InternalSyntaxToken operator, ExpressionTree operand) {
    return newOperatorAndOperand(operator, operand);
  }

  public ExpressionTree binaryExpression3(ExpressionTree expression, Optional> operatorAndOperands) {
    return binaryExpression(expression, operatorAndOperands);
  }

  public OperatorAndOperand newOperatorAndOperand3(InternalSyntaxToken operator, ExpressionTree operand) {
    return newOperatorAndOperand(operator, operand);
  }

  public ExpressionTree binaryExpression2(ExpressionTree expression, Optional> operatorAndOperands) {
    return binaryExpression(expression, operatorAndOperands);
  }

  public OperatorAndOperand newOperatorAndOperand2(InternalSyntaxToken operator, ExpressionTree operand) {
    return newOperatorAndOperand(operator, operand);
  }

  public ExpressionTree binaryExpression1(ExpressionTree expression, Optional> operatorAndOperands) {
    return binaryExpression(expression, operatorAndOperands);
  }

  public OperatorAndOperand newOperatorAndOperand1(InternalSyntaxToken operator, ExpressionTree operand) {
    return newOperatorAndOperand(operator, operand);
  }

  public ExpressionTree newPrefixedExpression(InternalSyntaxToken operatorToken, ExpressionTree expression) {
    return new InternalPrefixUnaryExpression(kindMaps.getPrefixOperator((JavaPunctuator) operatorToken.getGrammarRuleKey()), operatorToken, expression);
  }

  public ExpressionTree newPostfixExpression(ExpressionTree expression, Optional postfixOperator) {
    ExpressionTree result = expression;

    if (postfixOperator.isPresent()) {
      InternalSyntaxToken postfixOperatorToken = postfixOperator.get();
      result = new InternalPostfixUnaryExpression(kindMaps.getPostfixOperator((JavaPunctuator) postfixOperator.get().getGrammarRuleKey()), result, postfixOperatorToken);
    }

    return result;
  }

  public ExpressionTree newTildaExpression(InternalSyntaxToken tildaToken, ExpressionTree expression) {
    return new InternalPrefixUnaryExpression(Kind.BITWISE_COMPLEMENT, tildaToken, expression);
  }

  public ExpressionTree newBangExpression(InternalSyntaxToken bangToken, ExpressionTree expression) {
    return new InternalPrefixUnaryExpression(Kind.LOGICAL_COMPLEMENT, bangToken, expression);
  }

  public ExpressionTree completeCastExpression(InternalSyntaxToken openParenToken, TypeCastExpressionTreeImpl partial) {
    return partial.complete(openParenToken);
  }

  public TypeCastExpressionTreeImpl newBasicTypeCastExpression(PrimitiveTypeTreeImpl basicType, InternalSyntaxToken closeParenToken, ExpressionTree expression) {
    return new TypeCastExpressionTreeImpl(basicType, closeParenToken, expression);
  }

  public TypeCastExpressionTreeImpl newClassCastExpression(TypeTree type, Optional> classTypes, InternalSyntaxToken closeParenToken,
    ExpressionTree expression) {
    BoundListTreeImpl bounds = BoundListTreeImpl.emptyList();
    InternalSyntaxToken andToken = null;
    if (classTypes.isPresent()) {
      andToken = classTypes.get().first();
      bounds = classTypes.get().second();
    }
    return new TypeCastExpressionTreeImpl(type, andToken, bounds, closeParenToken, expression);
  }

  public ExpressionTree completeMethodReference(MethodReferenceTreeImpl partial, Optional typeArguments, InternalSyntaxToken newOrIdentifierToken) {
    TypeArguments typeArgs = null;
    if (typeArguments.isPresent()) {
      typeArgs = typeArguments.get();
    }
    partial.complete(typeArgs, new IdentifierTreeImpl(newOrIdentifierToken));
    return partial;
  }

  public MethodReferenceTreeImpl newSuperMethodReference(InternalSyntaxToken superToken, InternalSyntaxToken doubleColonToken) {
    IdentifierTree superIdentifier = new IdentifierTreeImpl(superToken);
    return new MethodReferenceTreeImpl(superIdentifier, doubleColonToken);
  }

  public MethodReferenceTreeImpl newTypeMethodReference(Tree type, InternalSyntaxToken doubleColonToken) {
    return new MethodReferenceTreeImpl(type, doubleColonToken);
  }

  public MethodReferenceTreeImpl newPrimaryMethodReference(ExpressionTree expression, InternalSyntaxToken doubleColonToken) {
    return new MethodReferenceTreeImpl(expression, doubleColonToken);
  }

  public ExpressionTree lambdaExpression(LambdaParameterListTreeImpl parameters, InternalSyntaxToken arrowToken, Tree body) {
    return new LambdaExpressionTreeImpl(
      parameters.openParenToken(),
      ImmutableList.builder().addAll(parameters).build(),
      parameters.closeParenToken(),
      arrowToken,
      body);
  }

  public LambdaParameterListTreeImpl newInferedParameters(
    InternalSyntaxToken openParenToken,
    Optional>>>> identifiersOpt,
    InternalSyntaxToken closeParenToken) {

    ImmutableList.Builder params = ImmutableList.builder();

    if (identifiersOpt.isPresent()) {
      Tuple>>> identifiers = identifiersOpt.get();

      VariableTreeImpl variable = identifiers.first();
      params.add(variable);

      VariableTreeImpl previousVariable = variable;
      if (identifiers.second().isPresent()) {
        for (Tuple identifier : identifiers.second().get()) {
          variable = identifier.second();
          params.add(variable);

          InternalSyntaxToken comma = identifier.first();
          previousVariable.setEndToken(comma);
          previousVariable = variable;
        }
      }
    }

    return new LambdaParameterListTreeImpl(openParenToken, params.build(), closeParenToken);
  }

  public LambdaParameterListTreeImpl formalLambdaParameters(FormalParametersListTreeImpl formalParameters) {
    return new LambdaParameterListTreeImpl(formalParameters.openParenToken(), formalParameters, formalParameters.closeParenToken());
  }

  public LambdaParameterListTreeImpl singleInferedParameter(VariableTreeImpl parameter) {
    return new LambdaParameterListTreeImpl(null, ImmutableList.of(parameter), null);
  }

  public VariableTreeImpl newSimpleParameter(InternalSyntaxToken identifierToken) {
    IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);
    return new VariableTreeImpl(identifier);
  }

  public ParenthesizedTreeImpl parenthesizedExpression(InternalSyntaxToken leftParenSyntaxToken, ExpressionTree expression, InternalSyntaxToken rightParenSyntaxToken) {
    return new ParenthesizedTreeImpl(leftParenSyntaxToken, expression, rightParenSyntaxToken);
  }

  public ExpressionTree newExpression(InternalSyntaxToken newToken, Optional> annotations, ExpressionTree partial) {
    TypeTree typeTree;
    if (partial.is(Tree.Kind.NEW_CLASS)) {
      NewClassTreeImpl newClassTree = (NewClassTreeImpl) partial;
      newClassTree.completeWithNewKeyword(newToken);
      typeTree = newClassTree.identifier();
    } else {
      NewArrayTreeImpl newArrayTree = (NewArrayTreeImpl) partial;
      newArrayTree.completeWithNewKeyword(newToken);
      typeTree = newArrayTree.type();
    }
    completeTypeTreeWithAnnotations(typeTree, annotations);
    return partial;
  }

  public ExpressionTree newClassCreator(Optional typeArguments, TypeTree qualifiedIdentifier, NewClassTreeImpl classCreatorRest) {
    if (typeArguments.isPresent()) {
      classCreatorRest.completeWithTypeArguments(typeArguments.get());
    }
    return classCreatorRest.completeWithIdentifier(qualifiedIdentifier);
  }

  public ExpressionTree newArrayCreator(TypeTree type, NewArrayTreeImpl partial) {
    return partial.complete(type);
  }

  public NewArrayTreeImpl completeArrayCreator(Optional> annotations, NewArrayTreeImpl partial) {
    if (annotations.isPresent()) {
      partial.completeFirstDimension(annotations.get());
    }
    return partial;
  }

  public NewArrayTreeImpl newArrayCreatorWithInitializer(
    InternalSyntaxToken openBracketToken, InternalSyntaxToken closeBracketToken,
    Optional>, Tuple>>> dimensions,
    NewArrayTreeImpl partial) {

    ImmutableList.Builder dDimensionsBuilder = ImmutableList.builder();
    dDimensionsBuilder.add(new ArrayDimensionTreeImpl(openBracketToken, null, closeBracketToken));
    if (dimensions.isPresent()) {
      for (Tuple>, Tuple> dim : dimensions.get()) {
        List annotations = dim.first().or(ImmutableList.of());
        Tuple brackets = dim.second();
        dDimensionsBuilder.add(new ArrayDimensionTreeImpl(annotations, brackets.first(), null, brackets.second()));
      }
    }

    return partial.completeDimensions(dDimensionsBuilder.build());
  }

  public NewArrayTreeImpl newArrayCreatorWithDimension(InternalSyntaxToken openBracketToken, ExpressionTree expression, InternalSyntaxToken closeBracketToken,
    Optional> arrayAccesses,
    Optional>, Tuple>>> dims) {

    ImmutableList.Builder dimensions = ImmutableList.builder();

    dimensions.add(new ArrayDimensionTreeImpl(openBracketToken, expression, closeBracketToken));
    if (arrayAccesses.isPresent()) {
      for (ArrayAccessExpressionTreeImpl arrayAccess : arrayAccesses.get()) {
        dimensions.add(arrayAccess.dimension());
      }
    }
    if (dims.isPresent()) {
      for (Tuple>, Tuple> dim : dims.get()) {
        Tuple brackets = dim.second();
        List annotations = dim.first().or(ImmutableList.of());
        dimensions.add(new ArrayDimensionTreeImpl(annotations, brackets.first(), null, brackets.second()));
      }
    }
    return new NewArrayTreeImpl(dimensions.build(), InitializerListTreeImpl.emptyList());
  }

  public ExpressionTree basicClassExpression(PrimitiveTypeTreeImpl basicType, Optional>> dimensions,
    InternalSyntaxToken dotToken, InternalSyntaxToken classToken) {
    // 15.8.2. Class Literals
    // int.class
    // int[].class

    IdentifierTreeImpl classIdentifier = new IdentifierTreeImpl(classToken);
    ArrayTypeTreeImpl nestedDimensions = newArrayTypeTree(dimensions);
    TypeTree typeTree = applyDim(basicType, nestedDimensions);
    return new MemberSelectExpressionTreeImpl((ExpressionTree) typeTree, dotToken, classIdentifier);
  }

  public PrimitiveTypeTreeImpl newBasicType(Optional> annotations, InternalSyntaxToken basicType) {
    JavaTree.PrimitiveTypeTreeImpl primitiveTypeTree = new JavaTree.PrimitiveTypeTreeImpl(basicType);
    completeTypeTreeWithAnnotations(primitiveTypeTree, annotations);
    return primitiveTypeTree;
  }

  public ArgumentListTreeImpl completeArguments(InternalSyntaxToken openParenthesisToken, Optional expressions, InternalSyntaxToken closeParenthesisToken) {
    return expressions.isPresent() ?
      expressions.get().complete(openParenthesisToken, closeParenthesisToken) :
      new ArgumentListTreeImpl(openParenthesisToken, closeParenthesisToken);
  }

  public ArgumentListTreeImpl newArguments(ExpressionTree expression, Optional>> rests) {
    ImmutableList.Builder expressions = ImmutableList.builder();
    expressions.add(expression);
    ImmutableList.Builder separators = ImmutableList.builder();
    if (rests.isPresent()) {
      for (Tuple rest : rests.get()) {
        separators.add(rest.first());
        expressions.add(rest.second());
      }
    }

    return new ArgumentListTreeImpl(expressions.build(), separators.build());
  }

  public TypeTree annotationIdentifier(InternalSyntaxToken firstIdentifier, Optional>> rests) {
    List children = Lists.newArrayList();
    children.add(firstIdentifier);
    if (rests.isPresent()) {
      for (Tuple rest : rests.get()) {
        children.add(rest.first());
        children.add(rest.second());
      }
    }

    JavaTree result = null;

    InternalSyntaxToken dotToken = null;
    for (InternalSyntaxToken child : children) {
      if (!child.getGrammarRuleKey().equals(JavaTokenType.IDENTIFIER)) {
        dotToken = child;
      } else {
        InternalSyntaxToken identifierToken = child;

        if (result == null) {
          result = new IdentifierTreeImpl(identifierToken);
        } else {
          IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);
          result = new MemberSelectExpressionTreeImpl((ExpressionTree) result, dotToken, identifier);
        }
      }
    }

    return (TypeTree) result;
  }

  public  T newQualifiedIdentifier(ExpressionTree firstIdentifier, Optional>> rests) {
    ExpressionTree result = firstIdentifier;

    if (rests.isPresent()) {
      for (Tuple rest : rests.get()) {
        InternalSyntaxToken dotToken = rest.first();
        if (rest.second().is(Kind.IDENTIFIER)) {
          result = new MemberSelectExpressionTreeImpl(result, dotToken, (IdentifierTreeImpl) rest.second());
        } else if (rest.second().is(Kind.PARAMETERIZED_TYPE)) {
          ParameterizedTypeTreeImpl parameterizedType = (ParameterizedTypeTreeImpl) rest.second();
          IdentifierTreeImpl identifier = (IdentifierTreeImpl) parameterizedType.type();

          result = new MemberSelectExpressionTreeImpl(result, dotToken, identifier);
          result = new ParameterizedTypeTreeImpl((TypeTree) result, (TypeArgumentListTreeImpl) parameterizedType.typeArguments());
        } else {
          throw new IllegalArgumentException();
        }
      }
      moveAnnotations(result, firstIdentifier);
    }

    return (T) result;
  }

  private static void moveAnnotations(ExpressionTree result, ExpressionTree firstIdentifier) {
    List firstIdentifierAnnotations;
    boolean isParameterizedType = firstIdentifier.is(Tree.Kind.PARAMETERIZED_TYPE);

    if (isParameterizedType) {
      firstIdentifierAnnotations = ((ParameterizedTypeTree) firstIdentifier).annotations();
    } else {
      firstIdentifierAnnotations = ((IdentifierTree) firstIdentifier).annotations();
    }
    // move the annotations from the first identifier to the member select or the parameterized type
    if (!firstIdentifierAnnotations.isEmpty()) {
      if (result.is(Tree.Kind.MEMBER_SELECT)) {
        ((MemberSelectExpressionTreeImpl) result).complete(firstIdentifierAnnotations);
      } else {
        ((ParameterizedTypeTreeImpl) result).complete(firstIdentifierAnnotations);
      }
      if (isParameterizedType) {
        ((ParameterizedTypeTreeImpl) firstIdentifier).complete(ImmutableList.of());
      } else {
        ((IdentifierTreeImpl) firstIdentifier).complete(ImmutableList.of());
      }
    }

  }

  public ExpressionTree newAnnotatedParameterizedIdentifier(
    Optional> annotations, InternalSyntaxToken identifierToken, Optional typeArguments) {

    List annotationList = annotations.isPresent() ?
      ImmutableList.builder().addAll(annotations.get()).build() :
      ImmutableList.of();

    ExpressionTree result = new IdentifierTreeImpl(identifierToken);

    if (typeArguments.isPresent()) {
      result = new ParameterizedTypeTreeImpl((TypeTree) result, typeArguments.get()).complete(annotationList);
    } else {
      result = ((IdentifierTreeImpl) result).complete(annotationList);
    }

    return result;
  }

  public NewArrayTreeImpl newArrayInitializer(
    InternalSyntaxToken openBraceToken,
    Optional optionalComma,
    Optional>>> rests,
    InternalSyntaxToken closeBraceToken) {
    ImmutableList.Builder initializers = ImmutableList.builder();
    ImmutableList.Builder separators = ImmutableList.builder();

    if (optionalComma.isPresent()) {
      separators.add(optionalComma.get());
    }
    if (rests.isPresent()) {
      for (Tuple> rest : rests.get()) {
        initializers.add(rest.first());
        if (rest.second().isPresent()) {
          separators.add(rest.second().get());
        }
      }
    }
    return new NewArrayTreeImpl(ImmutableList.of(),
      new InitializerListTreeImpl(initializers.build(), separators.build())).completeWithCurlyBraces(openBraceToken, closeBraceToken);
  }

  public QualifiedIdentifierListTreeImpl newQualifiedIdentifierList(TypeTree qualifiedIdentifier, Optional>> rests) {
    ImmutableList.Builder qualifiedIdentifiers = ImmutableList.builder();
    ImmutableList.Builder separators = ImmutableList.builder();
    qualifiedIdentifiers.add(qualifiedIdentifier);
    if (rests.isPresent()) {
      for (Tuple rest : rests.get()) {
        separators.add(rest.first());
        qualifiedIdentifiers.add(rest.second());
      }
    }
    return new QualifiedIdentifierListTreeImpl(qualifiedIdentifiers.build(), separators.build());
  }

  public ArrayAccessExpressionTreeImpl newArrayAccessExpression(Optional> annotations, InternalSyntaxToken openBracketToken, ExpressionTree index,
    InternalSyntaxToken closeBracketToken) {
    return new ArrayAccessExpressionTreeImpl(new ArrayDimensionTreeImpl(
      annotations.or(ImmutableList.of()),
      openBracketToken,
      index,
      closeBracketToken));
  }

  public NewClassTreeImpl newClassCreatorRest(ArgumentListTreeImpl arguments, Optional classBody) {
    return new NewClassTreeImpl(arguments, classBody.orNull());
  }

  public ExpressionTree newIdentifierOrMethodInvocation(Optional typeArguments, InternalSyntaxToken identifierToken,
    Optional arguments) {
    IdentifierTreeImpl identifier = new IdentifierTreeImpl(identifierToken);
    ExpressionTree result = identifier;
    if (arguments.isPresent()) {
      result = new MethodInvocationTreeImpl(identifier, typeArguments.orNull(), arguments.get());
    }
    return result;
  }

  public Tuple, ExpressionTree> completeMemberSelectOrMethodSelector(InternalSyntaxToken dotToken, ExpressionTree partial) {
    return newTuple(Optional.of(dotToken), partial);
  }

  public Tuple, ExpressionTree> completeCreatorSelector(InternalSyntaxToken dotToken, ExpressionTree partial) {
    ((NewClassTreeImpl) partial).completeWithDotToken(dotToken);
    return newTuple(Optional.absent(), partial);
  }

  public ExpressionTree newDotClassSelector(Optional>> dimensions,
    InternalSyntaxToken dotToken, InternalSyntaxToken classToken) {
    IdentifierTreeImpl identifier = new IdentifierTreeImpl(classToken);

    ArrayTypeTreeImpl nestedDimensions = newArrayTypeTree(dimensions);
    return new MemberSelectExpressionTreeImpl(nestedDimensions, dotToken, identifier);
  }

  private static ExpressionTree applySelectors(ExpressionTree primary, Optional, ExpressionTree>>> selectors) {
    ExpressionTree result = primary;

    if (selectors.isPresent()) {
      for (Tuple, ExpressionTree> tuple : selectors.get()) {
        Optional dotTokenOptional = tuple.first();
        ExpressionTree selector = tuple.second();

        if (dotTokenOptional.isPresent()) {
          InternalSyntaxToken dotToken = dotTokenOptional.get();

          if (selector.is(Kind.IDENTIFIER)) {
            IdentifierTreeImpl identifier = (IdentifierTreeImpl) selector;
            result = new MemberSelectExpressionTreeImpl(result, dotToken, identifier);
          } else {
            MethodInvocationTreeImpl methodInvocation = (MethodInvocationTreeImpl) selector;
            IdentifierTreeImpl identifier = (IdentifierTreeImpl) methodInvocation.methodSelect();
            MemberSelectExpressionTreeImpl memberSelect = new MemberSelectExpressionTreeImpl(result, dotToken, identifier);

            result = new MethodInvocationTreeImpl(memberSelect, methodInvocation.typeArguments(), (ArgumentListTreeImpl) methodInvocation.arguments());
          }
        } else if (selector.is(Kind.NEW_CLASS)) {
          NewClassTreeImpl newClass = (NewClassTreeImpl) selector;
          result = newClass.completeWithEnclosingExpression(result);
        } else if (selector.is(Kind.ARRAY_ACCESS_EXPRESSION)) {
          ArrayAccessExpressionTreeImpl arrayAccess = (ArrayAccessExpressionTreeImpl) selector;
          result = arrayAccess.complete(result);
        } else if (selector.is(Kind.MEMBER_SELECT)) {
          MemberSelectExpressionTreeImpl memberSelect = (MemberSelectExpressionTreeImpl) selector;
          result = memberSelect.completeWithExpression(result);
        } else {
          throw new IllegalStateException();
        }
      }
    }

    return result;
  }

  public ExpressionTree applySelectors1(ExpressionTree primary, Optional, ExpressionTree>>> selectors) {
    return applySelectors(primary, selectors);
  }

  public ExpressionTree applySelectors2(ExpressionTree primary, Optional, ExpressionTree>>> selectors) {
    return applySelectors(primary, selectors);
  }

  // End of expressions

  // Helpers

  public static class Tuple {
    private final T first;
    private final U second;

    public Tuple(T first, U second) {
      this.first = first;
      this.second = second;
    }

    public T first() {
      return first;
    }

    public U second() {
      return second;
    }
  }

  private  Tuple newTuple(T first, U second) {
    return new Tuple<>(first, second);
  }

  public  Tuple newTuple1(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple2(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple3(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple4(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple5(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple6(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple7(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple8(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple10(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple11(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple12(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple14(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple16(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple17(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple18(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple19(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple20(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple21(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple22(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple23(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple24(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple25(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple26(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple27(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple28(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newTuple29(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple newAnnotatedDimension(T first, U second) {
    return newTuple(first, second);
  }

  public  Tuple, U> newTupleAbsent1(U expression) {
    return newTuple(Optional.absent(), expression);
  }

  public  Tuple, U> newTupleAbsent2(U expression) {
    return newTuple(Optional.absent(), expression);
  }

  // End

  private static TypeTree applyDim(TypeTree expression, @Nullable ArrayTypeTreeImpl dim) {
    if (dim != null) {
      dim.setLastChildType(expression);
      return dim;
    } else {
      return expression;
    }
  }

  @CheckForNull
  private static ArrayTypeTreeImpl newArrayTypeTreeWithAnnotations(Optional>,
    Tuple>>> dims) {
    ArrayTypeTreeImpl result = null;
    if (dims.isPresent()) {
      for (Tuple>, Tuple> dim : dims.get()) {
        result = newArrayTypeTreeWithAnnotations(result, dim);
      }
    }
    return result;
  }

  private static ArrayTypeTreeImpl newArrayTypeTreeWithAnnotations(TypeTree type, Tuple>, Tuple> dim) {
    List annotations = dim.first().or(ImmutableList.of());
    InternalSyntaxToken openBracketToken = dim.second().first();
    InternalSyntaxToken closeBracketToken = dim.second().second();
    return new ArrayTypeTreeImpl(type, annotations, openBracketToken, closeBracketToken);
  }

  @CheckForNull
  private static ArrayTypeTreeImpl newArrayTypeTree(Optional>> dims) {
    ArrayTypeTreeImpl result = null;
    if (dims.isPresent()) {
      for (Tuple dim : dims.get()) {
        InternalSyntaxToken openBracketToken = dim.first();
        InternalSyntaxToken closeBracketToken = dim.second();
        result = new ArrayTypeTreeImpl(result, ImmutableList.of(), openBracketToken, closeBracketToken);
      }
    }
    return result;
  }

  private static void completeTypeTreeWithAnnotations(TypeTree type, Optional> annotations) {
    if (annotations.isPresent()) {
      List typeAnnotations = ImmutableList.builder().addAll(annotations.get()).build();
      completeTypeTreeWithAnnotations(type, typeAnnotations);
    }
  }

  private static void completeTypeTreeWithAnnotations(TypeTree type, List typeAnnotations) {
    if (type.is(Tree.Kind.IDENTIFIER)) {
      ((IdentifierTreeImpl) type).complete(typeAnnotations);
    } else if (type.is(Tree.Kind.MEMBER_SELECT)) {
      ((MemberSelectExpressionTreeImpl) type).complete(typeAnnotations);
    } else if (type.is(Tree.Kind.PARAMETERIZED_TYPE)) {
      ((ParameterizedTypeTreeImpl) type).complete(typeAnnotations);
    } else {
      ((PrimitiveTypeTreeImpl) type).complete(typeAnnotations);
    }
  }
}