
org.sonar.java.model.JParser Maven / Gradle / Ivy
/*
* SonarQube Java
* Copyright (C) 2012-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* 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 02110-1301, USA.
*/
package org.sonar.java.model;
import com.sonar.sslr.api.RecognitionException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTUtils;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.AssertStatement;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.BreakStatement;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.CharacterLiteral;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.ContinueStatement;
import org.eclipse.jdt.core.dom.CreationReference;
import org.eclipse.jdt.core.dom.Dimension;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EmptyStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.ExportsDirective;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionMethodReference;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.GuardedPattern;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IExtendedModifier;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.InstanceofExpression;
import org.eclipse.jdt.core.dom.IntersectionType;
import org.eclipse.jdt.core.dom.LabeledStatement;
import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.ModuleDeclaration;
import org.eclipse.jdt.core.dom.ModuleDirective;
import org.eclipse.jdt.core.dom.ModuleModifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NameQualifiedType;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.NullLiteral;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.OpensDirective;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.Pattern;
import org.eclipse.jdt.core.dom.PatternInstanceofExpression;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.ProvidesDirective;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.RecordDeclaration;
import org.eclipse.jdt.core.dom.RecordPattern;
import org.eclipse.jdt.core.dom.RequiresDirective;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.SuperMethodReference;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.SwitchExpression;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.SynchronizedStatement;
import org.eclipse.jdt.core.dom.TextBlock;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.ThrowStatement;
import org.eclipse.jdt.core.dom.TryStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.TypeMethodReference;
import org.eclipse.jdt.core.dom.TypeParameter;
import org.eclipse.jdt.core.dom.TypePattern;
import org.eclipse.jdt.core.dom.UnionType;
import org.eclipse.jdt.core.dom.UsesDirective;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WhileStatement;
import org.eclipse.jdt.core.dom.WildcardType;
import org.eclipse.jdt.core.dom.YieldStatement;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions;
import org.eclipse.jdt.internal.formatter.Token;
import org.eclipse.jdt.internal.formatter.TokenManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.java.ast.parser.ArgumentListTreeImpl;
import org.sonar.java.ast.parser.FormalParametersListTreeImpl;
import org.sonar.java.ast.parser.InitializerListTreeImpl;
import org.sonar.java.ast.parser.ModuleNameListTreeImpl;
import org.sonar.java.ast.parser.ModuleNameTreeImpl;
import org.sonar.java.ast.parser.QualifiedIdentifierListTreeImpl;
import org.sonar.java.ast.parser.ResourceListTreeImpl;
import org.sonar.java.ast.parser.StatementListTreeImpl;
import org.sonar.java.ast.parser.TypeParameterListTreeImpl;
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.ExportsDirectiveTreeImpl;
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.ModuleDeclarationTreeImpl;
import org.sonar.java.model.declaration.OpensDirectiveTreeImpl;
import org.sonar.java.model.declaration.ProvidesDirectiveTreeImpl;
import org.sonar.java.model.declaration.RequiresDirectiveTreeImpl;
import org.sonar.java.model.declaration.UsesDirectiveTreeImpl;
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.expression.VarTypeTreeImpl;
import org.sonar.java.model.pattern.DefaultPatternTreeImpl;
import org.sonar.java.model.pattern.GuardedPatternTreeImpl;
import org.sonar.java.model.pattern.NullPatternTreeImpl;
import org.sonar.java.model.pattern.RecordPatternTreeImpl;
import org.sonar.java.model.pattern.TypePatternTreeImpl;
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.SwitchExpressionTreeImpl;
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 org.sonar.java.model.statement.YieldStatementTreeImpl;
import org.sonar.plugins.java.api.tree.AnnotationTree;
import org.sonar.plugins.java.api.tree.ArrayDimensionTree;
import org.sonar.plugins.java.api.tree.ArrayTypeTree;
import org.sonar.plugins.java.api.tree.CatchTree;
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.InferedTypeTree;
import org.sonar.plugins.java.api.tree.ListTree;
import org.sonar.plugins.java.api.tree.Modifier;
import org.sonar.plugins.java.api.tree.ModifierTree;
import org.sonar.plugins.java.api.tree.ModuleDeclarationTree;
import org.sonar.plugins.java.api.tree.ModuleDirectiveTree;
import org.sonar.plugins.java.api.tree.PackageDeclarationTree;
import org.sonar.plugins.java.api.tree.PatternTree;
import org.sonar.plugins.java.api.tree.StatementTree;
import org.sonar.plugins.java.api.tree.SyntaxToken;
import org.sonar.plugins.java.api.tree.SyntaxTrivia;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.TypeParameterTree;
import org.sonar.plugins.java.api.tree.TypeTree;
import org.sonar.plugins.java.api.tree.VariableTree;
@SuppressWarnings({"rawtypes", "unchecked"})
public class JParser {
private static final Logger LOG = LoggerFactory.getLogger(JParser.class);
private static final Predicate IS_SYNTAX_ERROR = error -> (error.getID() & IProblem.Syntax) != 0;
private static final Predicate IS_UNDEFINED_TYPE_ERROR = error -> (error.getID() & IProblem.UndefinedType) != 0;
/**
* @param unitName see {@link ASTParser#setUnitName(String)}
* @throws RecognitionException in case of syntax errors
*/
public static JavaTree.CompilationUnitTreeImpl parse(ASTParser astParser, String version, String unitName, String source) {
astParser.setUnitName(unitName);
astParser.setSource(source.toCharArray());
CompilationUnit astNode;
try {
astNode = (CompilationUnit) astParser.createAST(null);
} catch (Exception e) {
LOG.error("ECJ: Unable to parse file", e);
throw new RecognitionException(-1, "ECJ: Unable to parse file.", e);
}
return convert(version, unitName, source, astNode);
}
static JavaTree.CompilationUnitTreeImpl convert(String version, String unitName, String source, CompilationUnit astNode) {
List errors = Stream.of(astNode.getProblems()).filter(IProblem::isError).collect(Collectors.toList());
Optional possibleSyntaxError = errors.stream().filter(IS_SYNTAX_ERROR).findFirst();
if (possibleSyntaxError.isPresent()) {
IProblem syntaxError = possibleSyntaxError.get();
int line = syntaxError.getSourceLineNumber();
int column = astNode.getColumnNumber(syntaxError.getSourceStart());
String message = String.format("Parse error at line %d column %d: %s", line, column, syntaxError.getMessage());
// interrupt parsing
throw new RecognitionException(line, message);
}
Set undefinedTypes = errors.stream()
.filter(IS_UNDEFINED_TYPE_ERROR)
.map(i -> new JProblem(
i.getMessage(),
(i.getID() & IProblem.PreviewFeatureUsed) != 0 ? JProblem.Type.PREVIEW_FEATURE_USED : JProblem.Type.UNDEFINED_TYPE))
.collect(Collectors.toSet());
JParser converter = new JParser();
converter.sema = new JSema(astNode.getAST());
converter.sema.undefinedTypes.addAll(undefinedTypes);
converter.compilationUnit = astNode;
converter.tokenManager = new TokenManager(lex(version, unitName, source.toCharArray()), source, new DefaultCodeFormatterOptions(new HashMap<>()));
JavaTree.CompilationUnitTreeImpl tree = converter.convertCompilationUnit(astNode);
tree.sema = converter.sema;
JWarning.Mapper.warningsFor(astNode).mappedInto(tree);
ASTUtils.mayTolerateMissingType(astNode.getAST());
setParents(tree);
return tree;
}
private static void setParents(Tree node) {
Iterator childrenIterator = iteratorFor(node);
while (childrenIterator.hasNext()) {
Tree child = childrenIterator.next();
((JavaTree) child).setParent(node);
setParents(child);
}
}
private static Iterator iteratorFor(Tree node) {
if (node.kind() == Tree.Kind.INFERED_TYPE || node.kind() == Tree.Kind.TOKEN) {
// getChildren throws exception in this case
return Collections.emptyIterator();
}
return ((JavaTree) node).getChildren().iterator();
}
private static List lex(String version, String unitName, char[] sourceChars) {
List tokens = new ArrayList<>();
Scanner scanner = new Scanner(
true,
false,
false,
CompilerOptions.versionToJdkLevel(version),
null,
null,
false
);
scanner.fakeInModule = "module-info.java".equals(unitName);
scanner.setSource(sourceChars);
while (true) {
try {
int tokenType = scanner.getNextToken();
Token token = Token.fromCurrent(scanner, tokenType);
tokens.add(token);
if (tokenType == TerminalTokens.TokenNameEOF) {
break;
}
} catch (InvalidInputException e) {
throw new IllegalStateException(e);
}
}
return tokens;
}
private CompilationUnit compilationUnit;
private TokenManager tokenManager;
private JSema sema;
private final Deque labels = new LinkedList<>();
private void declaration(@Nullable IBinding binding, Tree node) {
if (binding == null) {
return;
}
sema.declarations.put(binding, node);
}
private void usage(@Nullable IBinding binding, IdentifierTree node) {
if (binding == null) {
return;
}
binding = JSema.declarationBinding(binding);
sema.usages.computeIfAbsent(binding, k -> new ArrayList<>()).add(node);
}
private void usageLabel(@Nullable IdentifierTreeImpl node) {
if (node == null) {
return;
}
labels.stream()
.filter(symbol -> symbol.name().equals(node.name()))
.findFirst()
.ifPresent(labelSymbol -> {
labelSymbol.usages.add(node);
node.labelSymbol = labelSymbol;
});
}
private int firstTokenIndexAfter(ASTNode e) {
int index = tokenManager.firstIndexAfter(e, ANY_TOKEN);
while (tokenManager.get(index).isComment()) {
index++;
}
return index;
}
/**
* @param tokenType {@link TerminalTokens}
*/
private int nextTokenIndex(int tokenIndex, int tokenType) {
assert tokenType != ANY_TOKEN;
do {
tokenIndex += 1;
} while (tokenManager.get(tokenIndex).tokenType != tokenType);
return tokenIndex;
}
/**
* @param tokenType {@link TerminalTokens}
*/
private InternalSyntaxToken firstTokenBefore(ASTNode e, int tokenType) {
return createSyntaxToken(tokenManager.firstIndexBefore(e, tokenType));
}
/**
* @param tokenType {@link TerminalTokens}
*/
private InternalSyntaxToken firstTokenAfter(ASTNode e, int tokenType) {
return createSyntaxToken(tokenManager.firstIndexAfter(e, tokenType));
}
/**
* @param tokenType {@link TerminalTokens}
*/
private InternalSyntaxToken firstTokenIn(ASTNode e, int tokenType) {
return createSyntaxToken(tokenManager.firstIndexIn(e, tokenType));
}
/**
* @param tokenType {@link TerminalTokens}
*/
private InternalSyntaxToken lastTokenIn(ASTNode e, int tokenType) {
return createSyntaxToken(tokenManager.lastIndexIn(e, tokenType));
}
private InternalSyntaxToken createSyntaxToken(int tokenIndex) {
Token t = tokenManager.get(tokenIndex);
if (t.tokenType == TerminalTokens.TokenNameEOF) {
if (t.originalStart == 0) {
return new InternalSyntaxToken(1, 0, "", collectComments(tokenIndex), true);
}
final int position = t.originalStart - 1;
final char c = tokenManager.getSource().charAt(position);
int line = compilationUnit.getLineNumber(position);
int column = compilationUnit.getColumnNumber(position);
if (c == '\n' || c == '\r') {
line++;
column = 0;
} else {
column++;
}
return new InternalSyntaxToken(line, column, "", collectComments(tokenIndex), true);
}
return new InternalSyntaxToken(
compilationUnit.getLineNumber(t.originalStart),
compilationUnit.getColumnNumber(t.originalStart),
t.toString(tokenManager.getSource()),
collectComments(tokenIndex),
false
);
}
private InternalSyntaxToken createSpecialToken(int tokenIndex) {
Token t = tokenManager.get(tokenIndex);
List comments = t.tokenType == TerminalTokens.TokenNameGREATER
? collectComments(tokenIndex)
: Collections.emptyList();
return new InternalSyntaxToken(
compilationUnit.getLineNumber(t.originalEnd),
compilationUnit.getColumnNumber(t.originalEnd),
">",
comments,
false
);
}
private List collectComments(int tokenIndex) {
int commentIndex = tokenIndex;
while (commentIndex > 0 && tokenManager.get(commentIndex - 1).isComment()) {
commentIndex--;
}
List comments = new ArrayList<>();
for (int i = commentIndex; i < tokenIndex; i++) {
Token t = tokenManager.get(i);
comments.add(new InternalSyntaxTrivia(
t.toString(tokenManager.getSource()),
compilationUnit.getLineNumber(t.originalStart),
compilationUnit.getColumnNumber(t.originalStart)
));
}
return comments;
}
private void addEmptyStatementsToList(int tokenIndex, List list) {
while (true) {
Token token;
do {
tokenIndex++;
token = tokenManager.get(tokenIndex);
} while (token.isComment());
if (token.tokenType != TerminalTokens.TokenNameSEMICOLON) {
break;
}
list.add(new EmptyStatementTreeImpl(createSyntaxToken(tokenIndex)));
}
}
private JavaTree.CompilationUnitTreeImpl convertCompilationUnit(CompilationUnit e) {
PackageDeclarationTree packageDeclaration = null;
if (e.getPackage() != null) {
packageDeclaration = new JavaTree.PackageDeclarationTreeImpl(
convertAnnotations(e.getPackage().annotations()),
firstTokenIn(e.getPackage(), TerminalTokens.TokenNamepackage),
convertExpression(e.getPackage().getName()),
firstTokenIn(e.getPackage(), TerminalTokens.TokenNameSEMICOLON)
);
}
List imports = new ArrayList<>();
for (int i = 0; i < e.imports().size(); i++) {
ImportDeclaration e2 = (ImportDeclaration) e.imports().get(i);
ExpressionTree name = convertImportName(e2.getName());
if (e2.isOnDemand()) {
name = new MemberSelectExpressionTreeImpl(
name,
lastTokenIn(e2, TerminalTokens.TokenNameDOT),
new IdentifierTreeImpl(lastTokenIn(e2, TerminalTokens.TokenNameMULTIPLY))
);
}
JavaTree.ImportTreeImpl t = new JavaTree.ImportTreeImpl(
firstTokenIn(e2, TerminalTokens.TokenNameimport),
e2.isStatic() ? firstTokenIn(e2, TerminalTokens.TokenNamestatic) : null,
name,
lastTokenIn(e2, TerminalTokens.TokenNameSEMICOLON)
);
t.binding = e2.resolveBinding();
imports.add(t);
int tokenIndex = tokenManager.lastIndexIn(e2, TerminalTokens.TokenNameSEMICOLON);
addEmptyStatementsToList(tokenIndex, imports);
}
List types = new ArrayList<>();
for (Object type : e.types()) {
processBodyDeclaration((AbstractTypeDeclaration) type, types);
}
if (e.imports().isEmpty() && e.types().isEmpty()) {
addEmptyStatementsToList(-1, imports);
}
return new JavaTree.CompilationUnitTreeImpl(
packageDeclaration,
imports,
types,
convertModuleDeclaration(compilationUnit.getModule()),
firstTokenAfter(e, TerminalTokens.TokenNameEOF)
);
}
private ExpressionTree convertImportName(Name node) {
switch (node.getNodeType()) {
case ASTNode.SIMPLE_NAME: {
return new IdentifierTreeImpl(firstTokenIn(node, TerminalTokens.TokenNameIdentifier));
}
case ASTNode.QUALIFIED_NAME: {
QualifiedName e = (QualifiedName) node;
return new MemberSelectExpressionTreeImpl(
convertImportName(e.getQualifier()),
firstTokenAfter(e.getQualifier(), TerminalTokens.TokenNameDOT),
(IdentifierTreeImpl) convertImportName(e.getName()));
}
default:
throw new IllegalStateException(ASTNode.nodeClassForType(node.getNodeType()).toString());
}
}
@Nullable
private ModuleDeclarationTree convertModuleDeclaration(@Nullable ModuleDeclaration e) {
if (e == null) {
return null;
}
List moduleDirectives = new ArrayList<>();
for (Object o : e.moduleStatements()) {
moduleDirectives.add(
convertModuleDirective((ModuleDirective) o)
);
}
return new ModuleDeclarationTreeImpl(
convertAnnotations(e.annotations()),
e.isOpen() ? firstTokenIn(e, TerminalTokens.TokenNameopen) : null,
firstTokenBefore(e.getName(), TerminalTokens.TokenNamemodule),
convertModuleName(e.getName()),
firstTokenAfter(e.getName(), TerminalTokens.TokenNameLBRACE),
moduleDirectives,
lastTokenIn(e, TerminalTokens.TokenNameRBRACE)
);
}
private ModuleNameTreeImpl convertModuleName(Name node) {
switch (node.getNodeType()) {
case ASTNode.QUALIFIED_NAME: {
QualifiedName e = (QualifiedName) node;
ModuleNameTreeImpl t = convertModuleName(e.getQualifier());
t.add(new IdentifierTreeImpl(firstTokenIn(e.getName(), TerminalTokens.TokenNameIdentifier)));
return t;
}
case ASTNode.SIMPLE_NAME: {
ModuleNameTreeImpl t = ModuleNameTreeImpl.emptyList();
t.add(new IdentifierTreeImpl(firstTokenIn(node, TerminalTokens.TokenNameIdentifier)));
return t;
}
default:
throw new IllegalStateException(ASTNode.nodeClassForType(node.getNodeType()).toString());
}
}
private ModuleNameListTreeImpl convertModuleNames(List> list) {
ModuleNameListTreeImpl t = ModuleNameListTreeImpl.emptyList();
for (int i = 0; i < list.size(); i++) {
Name o = (Name) list.get(i);
if (i > 0) {
t.separators().add(firstTokenBefore(o, TerminalTokens.TokenNameCOMMA));
}
t.add(convertModuleName(o));
}
return t;
}
private ModuleDirectiveTree convertModuleDirective(ModuleDirective node) {
switch (node.getNodeType()) {
case ASTNode.REQUIRES_DIRECTIVE: {
RequiresDirective e = (RequiresDirective) node;
List modifiers = new ArrayList<>();
for (Object o : e.modifiers()) {
switch (((ModuleModifier) o).getKeyword().toString()) {
case "static":
modifiers.add(new ModifierKeywordTreeImpl(Modifier.STATIC, firstTokenIn((ASTNode) o, ANY_TOKEN)));
break;
case "transitive":
modifiers.add(new ModifierKeywordTreeImpl(Modifier.TRANSITIVE, firstTokenIn((ASTNode) o, ANY_TOKEN)));
break;
default:
throw new IllegalStateException();
}
}
return new RequiresDirectiveTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNamerequires),
new ModifiersTreeImpl(modifiers),
convertModuleName(e.getName()),
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
case ASTNode.EXPORTS_DIRECTIVE: {
ExportsDirective e = (ExportsDirective) node;
return new ExportsDirectiveTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNameexports),
convertExpression(e.getName()),
e.modules().isEmpty() ? null : firstTokenAfter(e.getName(), TerminalTokens.TokenNameto),
convertModuleNames(e.modules()),
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
case ASTNode.OPENS_DIRECTIVE: {
OpensDirective e = (OpensDirective) node;
return new OpensDirectiveTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNameopens),
convertExpression(e.getName()),
e.modules().isEmpty() ? null : firstTokenAfter(e.getName(), TerminalTokens.TokenNameto),
convertModuleNames(e.modules()),
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
case ASTNode.USES_DIRECTIVE: {
UsesDirective e = (UsesDirective) node;
return new UsesDirectiveTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNameuses),
(TypeTree) convertExpression(e.getName()),
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
case ASTNode.PROVIDES_DIRECTIVE: {
ProvidesDirective e = (ProvidesDirective) node;
QualifiedIdentifierListTreeImpl typeNames = QualifiedIdentifierListTreeImpl.emptyList();
for (int i = 0; i < e.implementations().size(); i++) {
Name o = (Name) e.implementations().get(i);
if (i > 0) {
typeNames.separators().add(firstTokenBefore(o, TerminalTokens.TokenNameCOMMA));
}
typeNames.add((TypeTree) convertExpression(o));
}
return new ProvidesDirectiveTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNameprovides),
(TypeTree) convertExpression(e.getName()),
firstTokenAfter(e.getName(), TerminalTokens.TokenNamewith),
typeNames,
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
default:
throw new IllegalStateException(ASTNode.nodeClassForType(node.getNodeType()).toString());
}
}
private ClassTreeImpl convertTypeDeclaration(AbstractTypeDeclaration e) {
List members = new ArrayList<>();
int leftBraceTokenIndex = findLeftBraceTokenIndex(e);
addEmptyStatementsToList(leftBraceTokenIndex, members);
for (Object o : e.bodyDeclarations()) {
processBodyDeclaration((BodyDeclaration) o, members);
}
ModifiersTreeImpl modifiers = convertModifiers(e.modifiers());
IdentifierTreeImpl name = createSimpleName(e.getName());
InternalSyntaxToken openBraceToken = createSyntaxToken(leftBraceTokenIndex);
InternalSyntaxToken closeBraceToken = lastTokenIn(e, TerminalTokens.TokenNameRBRACE);
final ClassTreeImpl t;
switch (e.getNodeType()) {
case ASTNode.TYPE_DECLARATION:
t = convertTypeDeclaration((TypeDeclaration) e, modifiers, name, openBraceToken, members, closeBraceToken);
break;
case ASTNode.ENUM_DECLARATION:
t = convertEnumDeclaration((EnumDeclaration) e, modifiers, name, openBraceToken, members, closeBraceToken);
break;
case ASTNode.RECORD_DECLARATION:
t = convertRecordDeclaration((RecordDeclaration) e, modifiers, name, openBraceToken, members, closeBraceToken);
break;
case ASTNode.ANNOTATION_TYPE_DECLARATION:
t = convertAnnotationTypeDeclaration((AnnotationTypeDeclaration) e, modifiers, name, openBraceToken, members, closeBraceToken);
break;
default:
throw new IllegalStateException(ASTNode.nodeClassForType(e.getNodeType()).toString());
}
// no-op for annotation-types
completeSuperInterfaces(e, t);
t.typeBinding = e.resolveBinding();
declaration(t.typeBinding, t);
return t;
}
private ClassTreeImpl convertTypeDeclaration(TypeDeclaration e, ModifiersTreeImpl modifiers, IdentifierTreeImpl name,
InternalSyntaxToken openBraceToken, List members, InternalSyntaxToken closeBraceToken) {
InternalSyntaxToken declarationKeyword = firstTokenBefore(e.getName(), e.isInterface() ? TerminalTokens.TokenNameinterface : TerminalTokens.TokenNameclass);
ClassTreeImpl t = new ClassTreeImpl(e.isInterface() ? Tree.Kind.INTERFACE : Tree.Kind.CLASS, openBraceToken, members, closeBraceToken)
.complete(modifiers, declarationKeyword, name)
.completeTypeParameters(convertTypeParameters(e.typeParameters()));
if (!e.permittedTypes().isEmpty()) {
List permittedTypes = e.permittedTypes();
InternalSyntaxToken permitsKeyword = firstTokenBefore((Type) permittedTypes.get(0), TerminalTokens.TokenNameRestrictedIdentifierpermits);
QualifiedIdentifierListTreeImpl classPermittedTypes = QualifiedIdentifierListTreeImpl.emptyList();
convertSeparatedTypeList(permittedTypes, classPermittedTypes);
t.completePermittedTypes(permitsKeyword, classPermittedTypes);
}
if (!e.isInterface() && e.getSuperclassType() != null) {
Type superclassType = e.getSuperclassType();
t.completeSuperclass(firstTokenBefore(superclassType, TerminalTokens.TokenNameextends), convertType(superclassType));
}
return t;
}
private ClassTreeImpl convertEnumDeclaration(EnumDeclaration e, ModifiersTreeImpl modifiers, IdentifierTreeImpl name,
InternalSyntaxToken openBraceToken, List members, InternalSyntaxToken closeBraceToken) {
List enumConstants = new ArrayList<>();
for (Object o : e.enumConstants()) {
// introduced as first members
enumConstants.add(processEnumConstantDeclaration((EnumConstantDeclaration) o));
}
members.addAll(0, enumConstants);
InternalSyntaxToken declarationKeyword = firstTokenBefore(e.getName(), TerminalTokens.TokenNameenum);
return new ClassTreeImpl(Tree.Kind.ENUM, openBraceToken, members, closeBraceToken)
.complete(modifiers, declarationKeyword, name);
}
private ClassTreeImpl convertAnnotationTypeDeclaration(AnnotationTypeDeclaration e, ModifiersTreeImpl modifiers, IdentifierTreeImpl name,
InternalSyntaxToken openBraceToken, List members, InternalSyntaxToken closeBraceToken) {
InternalSyntaxToken declarationKeyword = firstTokenBefore(e.getName(), TerminalTokens.TokenNameinterface);
return new ClassTreeImpl(Tree.Kind.ANNOTATION_TYPE, openBraceToken, members, closeBraceToken)
.complete(modifiers, declarationKeyword, name)
.completeAtToken(firstTokenBefore(e.getName(), TerminalTokens.TokenNameAT));
}
private ClassTreeImpl convertRecordDeclaration(RecordDeclaration e, ModifiersTreeImpl modifiers, IdentifierTreeImpl name,
InternalSyntaxToken openBraceToken, List members, InternalSyntaxToken closeBraceToken) {
InternalSyntaxToken declarationKeyword = firstTokenBefore(e.getName(), TerminalTokens.TokenNameRestrictedIdentifierrecord);
List recordComponents = e.recordComponents();
InternalSyntaxToken openParen = firstTokenAfter(e.getName(), TerminalTokens.TokenNameLPAREN);
InternalSyntaxToken closeParen = firstTokenAfter(
recordComponents.isEmpty() ? e.getName() : (ASTNode) recordComponents.get(recordComponents.size() - 1),
TerminalTokens.TokenNameRPAREN);
return new ClassTreeImpl(Tree.Kind.RECORD, openBraceToken, members, closeBraceToken)
.complete(modifiers, declarationKeyword, name)
.completeTypeParameters(convertTypeParameters(e.typeParameters()))
.completeRecordComponents(openParen, convertRecordComponents(e), closeParen);
}
private List convertRecordComponents(RecordDeclaration e) {
List recordComponents = new ArrayList<>();
for (int i = 0; i < e.recordComponents().size(); i++) {
SingleVariableDeclaration o = (SingleVariableDeclaration) e.recordComponents().get(i);
VariableTreeImpl recordComponent = convertVariable(o);
if (i < e.recordComponents().size() - 1) {
recordComponent.setEndToken(firstTokenAfter(o, TerminalTokens.TokenNameCOMMA));
}
recordComponents.add(recordComponent);
}
return recordComponents;
}
private int findLeftBraceTokenIndex(AbstractTypeDeclaration e) {
// TODO try to simplify, note that type annotations can contain LBRACE
if (e.getNodeType() == ASTNode.ENUM_DECLARATION) {
EnumDeclaration enumDeclaration = (EnumDeclaration) e;
if (!enumDeclaration.enumConstants().isEmpty()) {
return tokenManager.firstIndexBefore((ASTNode) enumDeclaration.enumConstants().get(0), TerminalTokens.TokenNameLBRACE);
}
if (!enumDeclaration.bodyDeclarations().isEmpty()) {
return tokenManager.firstIndexBefore((ASTNode) e.bodyDeclarations().get(0), TerminalTokens.TokenNameLBRACE);
}
return tokenManager.lastIndexIn(e, TerminalTokens.TokenNameLBRACE);
}
if (!e.bodyDeclarations().isEmpty()) {
return tokenManager.firstIndexBefore((ASTNode) e.bodyDeclarations().get(0), TerminalTokens.TokenNameLBRACE);
}
return tokenManager.lastIndexIn(e, TerminalTokens.TokenNameLBRACE);
}
private void completeSuperInterfaces(AbstractTypeDeclaration e, ClassTreeImpl t) {
List superInterfaces = superInterfaceTypes(e);
if (!superInterfaces.isEmpty()) {
QualifiedIdentifierListTreeImpl interfaces = QualifiedIdentifierListTreeImpl.emptyList();
convertSeparatedTypeList(superInterfaces, interfaces);
ASTNode firstInterface = (ASTNode) superInterfaces.get(0);
InternalSyntaxToken keyword = firstTokenBefore(firstInterface, t.is(Tree.Kind.INTERFACE) ? TerminalTokens.TokenNameextends : TerminalTokens.TokenNameimplements);
t.completeInterfaces(keyword, interfaces);
}
}
private static List> superInterfaceTypes(AbstractTypeDeclaration e) {
switch (e.getNodeType()) {
case ASTNode.TYPE_DECLARATION:
return ((TypeDeclaration) e).superInterfaceTypes();
case ASTNode.ENUM_DECLARATION:
return ((EnumDeclaration) e).superInterfaceTypes();
case ASTNode.RECORD_DECLARATION:
return ((RecordDeclaration) e).superInterfaceTypes();
case ASTNode.ANNOTATION_TYPE_DECLARATION:
return Collections.emptyList();
default:
throw new IllegalStateException(ASTNode.nodeClassForType(e.getNodeType()).toString());
}
}
private void convertSeparatedTypeList(List extends Type> source, ListTree target) {
for (int i = 0; i < source.size(); i++) {
Type o = source.get(i);
T tree = (T) convertType(o);
if (i > 0) {
target.separators().add(firstTokenBefore(o, TerminalTokens.TokenNameCOMMA));
}
target.add(tree);
}
}
private EnumConstantTreeImpl processEnumConstantDeclaration(EnumConstantDeclaration e) {
final int openParTokenIndex = firstTokenIndexAfter(e.getName());
final InternalSyntaxToken openParToken;
final InternalSyntaxToken closeParToken;
if (tokenManager.get(openParTokenIndex).tokenType == TerminalTokens.TokenNameLPAREN) {
openParToken = createSyntaxToken(openParTokenIndex);
closeParToken = e.arguments().isEmpty()
? firstTokenAfter(e.getName(), TerminalTokens.TokenNameRPAREN)
: firstTokenAfter((ASTNode) e.arguments().get(e.arguments().size() - 1), TerminalTokens.TokenNameRPAREN);
} else {
openParToken = null;
closeParToken = null;
}
final ArgumentListTreeImpl arguments = convertArguments(openParToken, e.arguments(), closeParToken);
ClassTreeImpl classBody = null;
if (e.getAnonymousClassDeclaration() != null) {
List members = new ArrayList<>();
for (Object o : e.getAnonymousClassDeclaration().bodyDeclarations()) {
processBodyDeclaration((BodyDeclaration) o, members);
}
classBody = new ClassTreeImpl(
Tree.Kind.CLASS,
firstTokenIn(e.getAnonymousClassDeclaration(), TerminalTokens.TokenNameLBRACE),
members,
lastTokenIn(e.getAnonymousClassDeclaration(), TerminalTokens.TokenNameRBRACE)
);
classBody.typeBinding = e.getAnonymousClassDeclaration().resolveBinding();
declaration(classBody.typeBinding, classBody);
}
final int separatorTokenIndex = firstTokenIndexAfter(e);
final InternalSyntaxToken separatorToken;
switch (tokenManager.get(separatorTokenIndex).tokenType) {
case TerminalTokens.TokenNameCOMMA:
case TerminalTokens.TokenNameSEMICOLON:
separatorToken = createSyntaxToken(separatorTokenIndex);
break;
case TerminalTokens.TokenNameRBRACE:
separatorToken = null;
break;
default:
throw new IllegalStateException();
}
IdentifierTreeImpl identifier = createSimpleName(e.getName());
if (e.getAnonymousClassDeclaration() == null) {
identifier.binding = excludeRecovery(e.resolveConstructorBinding(), arguments.size());
} else {
identifier.binding = findConstructorForAnonymousClass(e.getAST(), identifier.typeBinding, e.resolveConstructorBinding());
}
usage(identifier.binding, identifier);
EnumConstantTreeImpl t = new EnumConstantTreeImpl(
convertModifiers(e.modifiers()),
identifier,
new NewClassTreeImpl(identifier, arguments, classBody),
separatorToken
);
t.variableBinding = e.resolveVariable();
declaration(t.variableBinding, t);
return t;
}
private void processBodyDeclaration(BodyDeclaration node, List members) {
final int lastTokenIndex;
switch (node.getNodeType()) {
case ASTNode.ANNOTATION_TYPE_DECLARATION:
case ASTNode.ENUM_DECLARATION:
case ASTNode.RECORD_DECLARATION:
case ASTNode.TYPE_DECLARATION:
lastTokenIndex = processTypeDeclaration((AbstractTypeDeclaration) node, members);
break;
case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION:
lastTokenIndex = processAnnotationTypeMemberDeclaration((AnnotationTypeMemberDeclaration) node, members);
break;
case ASTNode.INITIALIZER:
lastTokenIndex = processInitializerDeclaration((Initializer) node, members);
break;
case ASTNode.METHOD_DECLARATION:
lastTokenIndex = processMethodDeclaration((MethodDeclaration) node, members);
break;
case ASTNode.FIELD_DECLARATION:
lastTokenIndex = processFieldDeclaration((FieldDeclaration) node, members);
break;
default:
throw new IllegalStateException(ASTNode.nodeClassForType(node.getNodeType()).toString());
}
addEmptyStatementsToList(lastTokenIndex, members);
}
private int processTypeDeclaration(AbstractTypeDeclaration node, List members) {
members.add(convertTypeDeclaration(node));
return tokenManager.lastIndexIn(node, TerminalTokens.TokenNameRBRACE);
}
private int processAnnotationTypeMemberDeclaration(AnnotationTypeMemberDeclaration e, List members) {
FormalParametersListTreeImpl parameters = new FormalParametersListTreeImpl(
firstTokenAfter(e.getName(), TerminalTokens.TokenNameLPAREN),
firstTokenAfter(e.getName(), TerminalTokens.TokenNameRPAREN));
Expression defaultExpression = e.getDefault();
InternalSyntaxToken defaultToken = defaultExpression == null ? null : firstTokenBefore(defaultExpression, TerminalTokens.TokenNamedefault);
ExpressionTree defaultValue = defaultExpression == null ? null : convertExpression(defaultExpression);
MethodTreeImpl t = new MethodTreeImpl(parameters, defaultToken, defaultValue)
.complete(convertType(e.getType()), createSimpleName(e.getName()), lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON))
.completeWithModifiers(convertModifiers(e.modifiers()));
t.methodBinding = e.resolveBinding();
declaration(t.methodBinding, t);
members.add(t);
return tokenManager.lastIndexIn(e, TerminalTokens.TokenNameSEMICOLON);
}
private int processInitializerDeclaration(Initializer e, List members) {
BlockTreeImpl blockTree = convertBlock(e.getBody());
if (org.eclipse.jdt.core.dom.Modifier.isStatic(e.getModifiers())) {
members.add(new StaticInitializerTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNamestatic),
(InternalSyntaxToken) blockTree.openBraceToken(),
blockTree.body(),
(InternalSyntaxToken) blockTree.closeBraceToken()));
} else {
members.add(new BlockTreeImpl(
Tree.Kind.INITIALIZER,
(InternalSyntaxToken) blockTree.openBraceToken(),
blockTree.body(),
(InternalSyntaxToken) blockTree.closeBraceToken()));
}
return tokenManager.lastIndexIn(e, TerminalTokens.TokenNameRBRACE);
}
private int processMethodDeclaration(MethodDeclaration e, List members) {
List p = e.parameters();
final FormalParametersListTreeImpl formalParameters;
if (e.isCompactConstructor()) {
// only used for records
formalParameters = new FormalParametersListTreeImpl(null, null);
} else {
InternalSyntaxToken openParen = firstTokenAfter(e.getName(), TerminalTokens.TokenNameLPAREN);
InternalSyntaxToken closeParen = firstTokenAfter(p.isEmpty() ? e.getName() : (ASTNode) p.get(p.size() - 1), TerminalTokens.TokenNameRPAREN);
formalParameters = new FormalParametersListTreeImpl(openParen, closeParen);
}
for (int i = 0; i < p.size(); i++) {
SingleVariableDeclaration o = (SingleVariableDeclaration) p.get(i);
VariableTreeImpl parameter = convertVariable(o);
if (i < p.size() - 1) {
parameter.setEndToken(firstTokenAfter(o, TerminalTokens.TokenNameCOMMA));
}
formalParameters.add(parameter);
}
QualifiedIdentifierListTreeImpl thrownExceptionTypes = QualifiedIdentifierListTreeImpl.emptyList();
List tt = e.thrownExceptionTypes();
convertSeparatedTypeList(tt, thrownExceptionTypes);
Block body = e.getBody();
Type returnType = e.getReturnType2();
InternalSyntaxToken throwsToken = tt.isEmpty() ? null : firstTokenBefore((Type) tt.get(0), TerminalTokens.TokenNamethrows);
InternalSyntaxToken semcolonToken = body == null ? lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON) : null;
MethodTreeImpl t = new MethodTreeImpl(
returnType == null ? null : applyExtraDimensions(convertType(returnType), e.extraDimensions()),
createSimpleName(e.getName()),
formalParameters,
throwsToken,
thrownExceptionTypes,
body == null ? null : convertBlock(body),
semcolonToken
).completeWithModifiers(
convertModifiers(e.modifiers())
).completeWithTypeParameters(
convertTypeParameters(e.typeParameters())
);
t.methodBinding = e.resolveBinding();
declaration(t.methodBinding, t);
members.add(t);
return tokenManager.lastIndexIn(e, body == null ? TerminalTokens.TokenNameSEMICOLON : TerminalTokens.TokenNameRBRACE);
}
private int processFieldDeclaration(FieldDeclaration fieldDeclaration, List members) {
ModifiersTreeImpl modifiers = convertModifiers(fieldDeclaration.modifiers());
TypeTree type = convertType(fieldDeclaration.getType());
for (int i = 0; i < fieldDeclaration.fragments().size(); i++) {
VariableDeclarationFragment fragment = (VariableDeclarationFragment) fieldDeclaration.fragments().get(i);
VariableTreeImpl t = new VariableTreeImpl(createSimpleName(fragment.getName()))
.completeModifiersAndType(modifiers, applyExtraDimensions(type, fragment.extraDimensions()));
if (fragment.getInitializer() != null) {
t.completeTypeAndInitializer(t.type(), firstTokenAfter(fragment.getName(), TerminalTokens.TokenNameEQUAL), convertExpression(fragment.getInitializer()));
}
t.setEndToken(firstTokenAfter(fragment, i + 1 < fieldDeclaration.fragments().size() ? TerminalTokens.TokenNameCOMMA : TerminalTokens.TokenNameSEMICOLON));
t.variableBinding = fragment.resolveBinding();
declaration(t.variableBinding, t);
members.add(t);
}
return tokenManager.lastIndexIn(fieldDeclaration, TerminalTokens.TokenNameSEMICOLON);
}
private ArgumentListTreeImpl convertArguments(@Nullable InternalSyntaxToken openParen, List> list, @Nullable InternalSyntaxToken closeParen) {
ArgumentListTreeImpl arguments = ArgumentListTreeImpl.emptyList().complete(openParen, closeParen);
for (int i = 0; i < list.size(); i++) {
Expression o = (Expression) list.get(i);
arguments.add(convertExpression(o));
if (i < list.size() - 1) {
arguments.separators().add(firstTokenAfter(o, TerminalTokens.TokenNameCOMMA));
}
}
return arguments;
}
@Nullable
private TypeArgumentListTreeImpl convertTypeArguments(List> list) {
if (list.isEmpty()) {
return null;
}
ASTNode last = (ASTNode) list.get(list.size() - 1);
int tokenIndex = tokenManager.firstIndexAfter(last, ANY_TOKEN);
while (tokenManager.get(tokenIndex).isComment()) {
tokenIndex++;
}
return convertTypeArguments(
firstTokenBefore((ASTNode) list.get(0), TerminalTokens.TokenNameLESS),
list,
// TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT vs TerminalTokens.TokenNameGREATER
createSpecialToken(tokenIndex)
);
}
private TypeArgumentListTreeImpl convertTypeArguments(InternalSyntaxToken l, List> list, InternalSyntaxToken g) {
TypeArgumentListTreeImpl typeArguments = new TypeArgumentListTreeImpl(l, g);
for (int i = 0; i < list.size(); i++) {
Type o = (Type) list.get(i);
if (i > 0) {
typeArguments.separators().add(firstTokenBefore(o, TerminalTokens.TokenNameCOMMA));
}
typeArguments.add(convertType(o));
}
return typeArguments;
}
private TypeParameterListTreeImpl convertTypeParameters(List> list) {
if (list.isEmpty()) {
return new TypeParameterListTreeImpl();
}
ASTNode last = (ASTNode) list.get(list.size() - 1);
int tokenIndex = tokenManager.firstIndexAfter(last, ANY_TOKEN);
while (tokenManager.get(tokenIndex).isComment()) {
tokenIndex++;
}
TypeParameterListTreeImpl t = new TypeParameterListTreeImpl(
firstTokenBefore((ASTNode) list.get(0), TerminalTokens.TokenNameLESS),
// TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT vs TerminalTokens.TokenNameGREATER
createSpecialToken(tokenIndex)
);
for (int i = 0; i < list.size(); i++) {
TypeParameter o = (TypeParameter) list.get(i);
if (i > 0) {
t.separators().add(firstTokenBefore(o, TerminalTokens.TokenNameCOMMA));
}
t.add(convertTypeParameter(o));
}
return t;
}
private TypeParameterTree convertTypeParameter(TypeParameter e) {
IdentifierTreeImpl i = createSimpleName(e.getName());
// TODO why ECJ uses IExtendedModifier here instead of Annotation ?
i.complete(convertAnnotations(e.modifiers()));
TypeParameterTreeImpl t;
List> typeBounds = e.typeBounds();
if (typeBounds.isEmpty()) {
t = new TypeParameterTreeImpl(i);
} else {
QualifiedIdentifierListTreeImpl bounds = QualifiedIdentifierListTreeImpl.emptyList();
for (int j = 0; j < typeBounds.size(); j++) {
Object o = typeBounds.get(j);
bounds.add(convertType((Type) o));
if (j < typeBounds.size() - 1) {
bounds.separators().add(firstTokenAfter((ASTNode) o, TerminalTokens.TokenNameAND));
}
}
t = new TypeParameterTreeImpl(
i,
firstTokenAfter(e.getName(), TerminalTokens.TokenNameextends),
bounds
);
}
t.typeBinding = e.resolveBinding();
return t;
}
/**
* @param extraDimensions list of {@link org.eclipse.jdt.core.dom.Dimension}
*/
private TypeTree applyExtraDimensions(TypeTree type, List> extraDimensions) {
ITypeBinding typeBinding = ((AbstractTypedTree) type).typeBinding;
for (int i = 0; i < extraDimensions.size(); i++) {
Dimension e = (Dimension) extraDimensions.get(i);
type = new JavaTree.ArrayTypeTreeImpl(
type,
(List) convertAnnotations(e.annotations()),
firstTokenIn(e, TerminalTokens.TokenNameLBRACKET),
firstTokenIn(e, TerminalTokens.TokenNameRBRACKET)
);
if (typeBinding != null) {
((JavaTree.ArrayTypeTreeImpl) type).typeBinding = typeBinding.createArrayType(i + 1);
}
}
return type;
}
private VariableTreeImpl convertVariable(SingleVariableDeclaration e) {
// TODO are extraDimensions and varargs mutually exclusive?
TypeTree type = convertType(e.getType());
type = applyExtraDimensions(type, e.extraDimensions());
if (e.isVarargs()) {
ITypeBinding typeBinding = ((AbstractTypedTree) type).typeBinding;
type = new JavaTree.ArrayTypeTreeImpl(
type,
(List) convertAnnotations(e.varargsAnnotations()),
firstTokenAfter(e.getType(), TerminalTokens.TokenNameELLIPSIS)
);
if (typeBinding != null) {
((JavaTree.ArrayTypeTreeImpl) type).typeBinding = typeBinding.createArrayType(1);
}
}
VariableTreeImpl t = new VariableTreeImpl(
convertModifiers(e.modifiers()),
type,
createSimpleName(e.getName())
);
if (e.getInitializer() != null) {
t.completeTypeAndInitializer(
t.type(),
firstTokenAfter(e.getName(), TerminalTokens.TokenNameEQUAL),
convertExpression(e.getInitializer())
);
}
t.variableBinding = e.resolveBinding();
declaration(t.variableBinding, t);
return t;
}
private void addVariableToList(VariableDeclarationExpression e2, List list) {
ModifiersTreeImpl modifiers = convertModifiers(e2.modifiers());
TypeTree type = convertType(e2.getType());
for (int i = 0; i < e2.fragments().size(); i++) {
VariableDeclarationFragment fragment = (VariableDeclarationFragment) e2.fragments().get(i);
VariableTreeImpl t = new VariableTreeImpl(createSimpleName(fragment.getName()));
t.completeModifiers(modifiers);
if (fragment.getInitializer() == null) {
t.completeType(applyExtraDimensions(type, fragment.extraDimensions()));
} else {
t.completeTypeAndInitializer(
applyExtraDimensions(type, fragment.extraDimensions()),
firstTokenBefore(fragment.getInitializer(), TerminalTokens.TokenNameEQUAL),
convertExpression(fragment.getInitializer())
);
}
if (i < e2.fragments().size() - 1) {
t.setEndToken(firstTokenAfter(fragment, TerminalTokens.TokenNameCOMMA));
}
t.variableBinding = fragment.resolveBinding();
declaration(t.variableBinding, t);
list.add(t);
}
}
private VarTypeTreeImpl convertVarType(SimpleType simpleType) {
VarTypeTreeImpl varTree = new VarTypeTreeImpl(firstTokenIn(simpleType.getName(), TerminalTokens.TokenNameIdentifier));
varTree.typeBinding = simpleType.resolveBinding();
return varTree;
}
private IdentifierTreeImpl createSimpleName(SimpleName e) {
IdentifierTreeImpl t = new IdentifierTreeImpl(firstTokenIn(e, TerminalTokens.TokenNameIdentifier));
t.typeBinding = e.resolveTypeBinding();
t.binding = e.resolveBinding();
return t;
}
private BlockTreeImpl convertBlock(Block e) {
List statements = new ArrayList<>();
for (Object o : e.statements()) {
addStatementToList((Statement) o, statements);
}
return new BlockTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNameLBRACE),
statements,
lastTokenIn(e, TerminalTokens.TokenNameRBRACE)
);
}
private void addStatementToList(Statement node, List statements) {
if (node.getNodeType() == ASTNode.VARIABLE_DECLARATION_STATEMENT) {
VariableDeclarationStatement e = (VariableDeclarationStatement) node;
TypeTree tType = convertType(e.getType());
ModifiersTreeImpl modifiers = convertModifiers(e.modifiers());
for (int i = 0; i < e.fragments().size(); i++) {
VariableDeclarationFragment fragment = (VariableDeclarationFragment) e.fragments().get(i);
VariableTreeImpl t = new VariableTreeImpl(createSimpleName(fragment.getName()))
.completeType(applyExtraDimensions(tType, fragment.extraDimensions()))
.completeModifiers(modifiers);
Expression initalizer = fragment.getInitializer();
if (initalizer != null) {
InternalSyntaxToken equalToken = firstTokenAfter(fragment.getName(), TerminalTokens.TokenNameEQUAL);
t.completeTypeAndInitializer(t.type(), equalToken, convertExpression(initalizer));
}
int endTokenType = i < e.fragments().size() - 1 ? TerminalTokens.TokenNameCOMMA : TerminalTokens.TokenNameSEMICOLON;
t.setEndToken(firstTokenAfter(fragment, endTokenType));
t.variableBinding = fragment.resolveBinding();
declaration(t.variableBinding, t);
statements.add(t);
}
} else if (node.getNodeType() == ASTNode.BREAK_STATEMENT && node.getLength() < "break".length()) {
// skip implicit break-statement
} else {
statements.add(createStatement(node));
}
}
private StatementTree createStatement(Statement node) {
switch (node.getNodeType()) {
case ASTNode.BLOCK:
return convertBlock((Block) node);
case ASTNode.EMPTY_STATEMENT:
return convertEmptyStatement((EmptyStatement) node);
case ASTNode.RETURN_STATEMENT:
return convertReturn((ReturnStatement) node);
case ASTNode.FOR_STATEMENT:
return convertFor((ForStatement) node);
case ASTNode.WHILE_STATEMENT:
return convertWhile((WhileStatement) node);
case ASTNode.IF_STATEMENT:
return convertIf((IfStatement) node);
case ASTNode.BREAK_STATEMENT:
return convertBreak((BreakStatement) node);
case ASTNode.DO_STATEMENT:
return convertDoWhile((DoStatement) node);
case ASTNode.ASSERT_STATEMENT:
return convertAssert((AssertStatement) node);
case ASTNode.SWITCH_STATEMENT:
return convertSwitchStatement((SwitchStatement) node);
case ASTNode.SYNCHRONIZED_STATEMENT:
return convertSynchronized((SynchronizedStatement) node);
case ASTNode.EXPRESSION_STATEMENT:
return convertExpressionStatement((ExpressionStatement) node);
case ASTNode.CONTINUE_STATEMENT:
return convertContinue((ContinueStatement) node);
case ASTNode.LABELED_STATEMENT:
return convertLabel((LabeledStatement) node);
case ASTNode.ENHANCED_FOR_STATEMENT:
return convertForeach((EnhancedForStatement) node);
case ASTNode.THROW_STATEMENT:
return convertThrow((ThrowStatement) node);
case ASTNode.TRY_STATEMENT:
return convertTry((TryStatement) node);
case ASTNode.TYPE_DECLARATION_STATEMENT:
return convertTypeDeclaration(((TypeDeclarationStatement) node).getDeclaration());
case ASTNode.CONSTRUCTOR_INVOCATION:
return convertConstructorInvocation((ConstructorInvocation) node);
case ASTNode.SUPER_CONSTRUCTOR_INVOCATION:
return convertSuperConstructorInvocation((SuperConstructorInvocation) node);
case ASTNode.YIELD_STATEMENT:
return convertYield((YieldStatement) node);
default:
throw new IllegalStateException(ASTNode.nodeClassForType(node.getNodeType()).toString());
}
}
private EmptyStatementTreeImpl convertEmptyStatement(EmptyStatement e) {
return new EmptyStatementTreeImpl(lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON));
}
private ReturnStatementTreeImpl convertReturn(ReturnStatement e) {
Expression expression = e.getExpression();
return new ReturnStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNamereturn),
expression == null ? null : convertExpression(expression),
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
private ForStatementTreeImpl convertFor(ForStatement e) {
StatementListTreeImpl forInitStatement = StatementListTreeImpl.emptyList();
for (int i = 0; i < e.initializers().size(); i++) {
Expression o = (Expression) e.initializers().get(i);
if (i > 0) {
forInitStatement.separators().add(firstTokenBefore(o, TerminalTokens.TokenNameCOMMA));
}
if (ASTNode.VARIABLE_DECLARATION_EXPRESSION == o.getNodeType()) {
addVariableToList((VariableDeclarationExpression) o, forInitStatement);
} else {
forInitStatement.add(new ExpressionStatementTreeImpl(convertExpression(o), null));
}
}
StatementListTreeImpl forUpdateStatement = StatementListTreeImpl.emptyList();
for (int i = 0; i < e.updaters().size(); i++) {
Expression o = (Expression) e.updaters().get(i);
if (i > 0) {
forUpdateStatement.separators().add(firstTokenBefore(o, TerminalTokens.TokenNameCOMMA));
}
forUpdateStatement.add(new ExpressionStatementTreeImpl(convertExpression(o), null));
}
final int firstSemicolonTokenIndex = e.initializers().isEmpty()
? tokenManager.firstIndexIn(e, TerminalTokens.TokenNameSEMICOLON)
: tokenManager.firstIndexAfter((ASTNode) e.initializers().get(e.initializers().size() - 1), TerminalTokens.TokenNameSEMICOLON);
Expression expression = e.getExpression();
final int secondSemicolonTokenIndex = expression == null
? nextTokenIndex(firstSemicolonTokenIndex, TerminalTokens.TokenNameSEMICOLON)
: tokenManager.firstIndexAfter(expression, TerminalTokens.TokenNameSEMICOLON);
return new ForStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNamefor),
firstTokenIn(e, TerminalTokens.TokenNameLPAREN),
forInitStatement,
createSyntaxToken(firstSemicolonTokenIndex),
expression == null ? null : convertExpression(expression),
createSyntaxToken(secondSemicolonTokenIndex),
forUpdateStatement,
firstTokenBefore(e.getBody(), TerminalTokens.TokenNameRPAREN),
createStatement(e.getBody())
);
}
private WhileStatementTreeImpl convertWhile(WhileStatement e) {
Expression expression = e.getExpression();
return new WhileStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNamewhile),
firstTokenBefore(expression, TerminalTokens.TokenNameLPAREN),
convertExpression(expression),
firstTokenAfter(expression, TerminalTokens.TokenNameRPAREN),
createStatement(e.getBody())
);
}
private IfStatementTreeImpl convertIf(IfStatement e) {
Expression expression = e.getExpression();
Statement thenStatement = e.getThenStatement();
Statement elseStatement = e.getElseStatement();
return new IfStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNameif),
firstTokenBefore(expression, TerminalTokens.TokenNameLPAREN),
convertExpression(expression),
firstTokenAfter(expression, TerminalTokens.TokenNameRPAREN),
createStatement(thenStatement),
elseStatement == null ? null : firstTokenAfter(thenStatement, TerminalTokens.TokenNameelse),
elseStatement == null ? null : createStatement(elseStatement)
);
}
private BreakStatementTreeImpl convertBreak(BreakStatement e) {
IdentifierTreeImpl identifier = e.getLabel() == null ? null : createSimpleName(e.getLabel());
usageLabel(identifier);
return new BreakStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNamebreak),
identifier,
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
private DoWhileStatementTreeImpl convertDoWhile(DoStatement e) {
Statement body = e.getBody();
Expression expression = e.getExpression();
return new DoWhileStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNamedo),
createStatement(body),
firstTokenAfter(body, TerminalTokens.TokenNamewhile),
firstTokenBefore(expression, TerminalTokens.TokenNameLPAREN),
convertExpression(expression),
firstTokenAfter(expression, TerminalTokens.TokenNameRPAREN),
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
private AssertStatementTreeImpl convertAssert(AssertStatement e) {
Expression message = e.getMessage();
AssertStatementTreeImpl t = new AssertStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNameassert),
convertExpression(e.getExpression()),
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
if (message != null) {
t.complete(firstTokenBefore(message, TerminalTokens.TokenNameCOLON), convertExpression(message));
}
return t;
}
private SwitchStatementTreeImpl convertSwitchStatement(SwitchStatement e) {
Expression expression = e.getExpression();
return new SwitchStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNameswitch),
firstTokenBefore(expression, TerminalTokens.TokenNameLPAREN),
convertExpression(expression),
firstTokenAfter(expression, TerminalTokens.TokenNameRPAREN),
firstTokenAfter(expression, TerminalTokens.TokenNameLBRACE),
convertSwitchStatements(e.statements()),
lastTokenIn(e, TerminalTokens.TokenNameRBRACE)
);
}
private List convertSwitchStatements(List> list) {
List groups = new ArrayList<>();
List caselabels = null;
StatementListTreeImpl body = null;
for (Object o : list) {
if (o instanceof SwitchCase) {
if (caselabels == null) {
caselabels = new ArrayList<>();
body = StatementListTreeImpl.emptyList();
}
SwitchCase c = (SwitchCase) o;
List expressions = new ArrayList<>();
for (Object oo : c.expressions()) {
expressions.add(convertExpressionFromCase((Expression) oo));
}
caselabels.add(new CaseLabelTreeImpl(
firstTokenIn(c, c.isDefault() ? TerminalTokens.TokenNamedefault : TerminalTokens.TokenNamecase),
expressions,
lastTokenIn(c, /* TerminalTokens.TokenNameCOLON or TerminalTokens.TokenNameARROW */ ANY_TOKEN)
));
} else {
if (caselabels != null) {
groups.add(new CaseGroupTreeImpl(caselabels, body));
}
caselabels = null;
addStatementToList((Statement) o, Objects.requireNonNull(body));
}
}
if (caselabels != null) {
groups.add(new CaseGroupTreeImpl(caselabels, body));
}
return groups;
}
private ExpressionTree convertExpressionFromCase(Expression e) {
if (e.getNodeType() == ASTNode.CASE_DEFAULT_EXPRESSION) {
return new DefaultPatternTreeImpl(firstTokenIn(e, TerminalTokens.TokenNamedefault));
}
if (e.getNodeType() == ASTNode.NULL_LITERAL) {
return new NullPatternTreeImpl((LiteralTreeImpl) convertExpression(e));
}
if (e instanceof Pattern) {
return convertPattern((Pattern) e);
}
return convertExpression(e);
}
private PatternTree convertPattern(Pattern p) {
switch (p.getNodeType()) {
case ASTNode.TYPE_PATTERN:
return new TypePatternTreeImpl(convertVariable(((TypePattern) p).getPatternVariable()));
case ASTNode.RECORD_PATTERN:
RecordPattern recordPattern = (RecordPattern) p;
List nestedPatterns = recordPattern.patterns().stream()
.map(this::convertPattern)
.collect(Collectors.toList());
TypeTree patternType = convertType(recordPattern.getPatternType());
IdentifierTreeImpl recordName = recordPattern.getPatternName() != null ? convertSimpleName(recordPattern.getPatternName()) : null;
return new RecordPatternTreeImpl(patternType, nestedPatterns, recordName);
case ASTNode.GUARDED_PATTERN:
GuardedPattern g = (GuardedPattern) p;
return new GuardedPatternTreeImpl(
convertPattern(g.getPattern()),
// FIXME java 19 support: should be "TerminalTokens.TokenNameRestrictedIdentifierWhen" instead of "TerminalTokens.TokenNameIdentifier"
firstTokenBefore(g.getExpression(), TerminalTokens.TokenNameIdentifier),
convertExpression(g.getExpression()));
case ASTNode.NULL_PATTERN:
// It is not clear how to reach this one, it seems to be possible only with badly constructed AST
// fall-through. Do nothing for now.
default:
// JEP-405 (not released as part of any JDK yet): ArrayPattern, RecordPattern
throw new IllegalStateException(ASTNode.nodeClassForType(p.getNodeType()).toString());
}
}
private SynchronizedStatementTreeImpl convertSynchronized(SynchronizedStatement e) {
Expression expression = e.getExpression();
return new SynchronizedStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNamesynchronized),
firstTokenBefore(expression, TerminalTokens.TokenNameLPAREN),
convertExpression(expression),
firstTokenAfter(expression, TerminalTokens.TokenNameRPAREN),
convertBlock(e.getBody())
);
}
private ExpressionStatementTreeImpl convertExpressionStatement(ExpressionStatement e) {
return new ExpressionStatementTreeImpl(
convertExpression(e.getExpression()),
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
private ContinueStatementTreeImpl convertContinue(ContinueStatement e) {
SimpleName label = e.getLabel();
IdentifierTreeImpl i = label == null ? null : createSimpleName(label);
usageLabel(i);
return new ContinueStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNamecontinue),
i,
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
private LabeledStatementTreeImpl convertLabel(LabeledStatement e) {
IdentifierTreeImpl i = createSimpleName(e.getLabel());
JLabelSymbol symbol = new JLabelSymbol(i.name());
labels.push(symbol);
LabeledStatementTreeImpl t = new LabeledStatementTreeImpl(
i,
firstTokenAfter(e.getLabel(), TerminalTokens.TokenNameCOLON),
createStatement(e.getBody())
);
labels.pop();
symbol.declaration = t;
t.labelSymbol = symbol;
return t;
}
private ForEachStatementImpl convertForeach(EnhancedForStatement e) {
SingleVariableDeclaration parameter = e.getParameter();
Expression expression = e.getExpression();
return new ForEachStatementImpl(
firstTokenIn(e, TerminalTokens.TokenNamefor),
firstTokenBefore(parameter, TerminalTokens.TokenNameLPAREN),
convertVariable(parameter),
firstTokenAfter(parameter, TerminalTokens.TokenNameCOLON),
convertExpression(expression),
firstTokenAfter(expression, TerminalTokens.TokenNameRPAREN),
createStatement(e.getBody())
);
}
private ThrowStatementTreeImpl convertThrow(ThrowStatement e) {
return new ThrowStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNamethrow),
convertExpression(e.getExpression()),
firstTokenAfter(e.getExpression(), TerminalTokens.TokenNameSEMICOLON)
);
}
private TryStatementTreeImpl convertTry(TryStatement e) {
ResourceListTreeImpl resources = convertResources(e);
List catches = convertCatchClauses(e);
Block f = e.getFinally();
return new TryStatementTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNametry),
resources.isEmpty() ? null : firstTokenIn(e, TerminalTokens.TokenNameLPAREN),
resources,
resources.isEmpty() ? null : firstTokenBefore(e.getBody(), TerminalTokens.TokenNameRPAREN),
convertBlock(e.getBody()),
catches,
f == null ? null : firstTokenBefore(f, TerminalTokens.TokenNamefinally),
f == null ? null : convertBlock(f)
);
}
private ResourceListTreeImpl convertResources(TryStatement e) {
List r = e.resources();
ResourceListTreeImpl resources = ResourceListTreeImpl.emptyList();
for (int i = 0; i < r.size(); i++) {
Expression o = (Expression) r.get(i);
if (ASTNode.VARIABLE_DECLARATION_EXPRESSION == o.getNodeType()) {
addVariableToList((VariableDeclarationExpression) o, resources);
} else {
resources.add(convertExpression(o));
}
addSeparatorToList(e, o, resources.separators(), i < e.resources().size() - 1);
}
return resources;
}
private void addSeparatorToList(TryStatement tryStatement, Expression resource, List separators, boolean isLast) {
if (isLast) {
separators.add(firstTokenAfter(resource, TerminalTokens.TokenNameSEMICOLON));
} else {
int tokenIndex = tokenManager.firstIndexBefore(tryStatement.getBody(), TerminalTokens.TokenNameRPAREN);
while (true) {
Token token;
do {
tokenIndex--;
token = tokenManager.get(tokenIndex);
} while (token.isComment());
if (token.tokenType != TerminalTokens.TokenNameSEMICOLON) {
break;
}
separators.add(createSyntaxToken(tokenIndex));
}
}
}
private List convertCatchClauses(TryStatement e) {
List catches = new ArrayList<>();
for (Object o : e.catchClauses()) {
CatchClause c = (CatchClause) o;
catches.add(new CatchTreeImpl(
firstTokenIn(c, TerminalTokens.TokenNamecatch),
firstTokenBefore(c.getException(), TerminalTokens.TokenNameLPAREN),
convertVariable(c.getException()),
firstTokenAfter(c.getException(), TerminalTokens.TokenNameRPAREN),
convertBlock(c.getBody())
));
}
return catches;
}
private ExpressionStatementTreeImpl convertConstructorInvocation(ConstructorInvocation e) {
ArgumentListTreeImpl arguments = convertArguments(
e.arguments().isEmpty() ? lastTokenIn(e, TerminalTokens.TokenNameLPAREN) : firstTokenBefore((ASTNode) e.arguments().get(0), TerminalTokens.TokenNameLPAREN),
e.arguments(),
lastTokenIn(e, TerminalTokens.TokenNameRPAREN)
);
IdentifierTreeImpl i = new IdentifierTreeImpl(e.arguments().isEmpty()
? lastTokenIn(e, TerminalTokens.TokenNamethis)
: firstTokenBefore((ASTNode) e.arguments().get(0), TerminalTokens.TokenNamethis));
MethodInvocationTreeImpl t = new MethodInvocationTreeImpl(
i,
convertTypeArguments(e.typeArguments()),
arguments
);
t.methodBinding = e.resolveConstructorBinding();
if (t.methodBinding != null) {
t.typeBinding = t.methodBinding.getDeclaringClass();
t.methodBinding = excludeRecovery(t.methodBinding, arguments.size());
}
i.binding = t.methodBinding;
usage(i.binding, i);
return new ExpressionStatementTreeImpl(
t,
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
private ExpressionStatementTreeImpl convertSuperConstructorInvocation(SuperConstructorInvocation e) {
IdentifierTreeImpl i = new IdentifierTreeImpl(firstTokenIn(e, TerminalTokens.TokenNamesuper));
ExpressionTree methodSelect = i;
if (e.getExpression() != null) {
methodSelect = new MemberSelectExpressionTreeImpl(
convertExpression(e.getExpression()),
firstTokenAfter(e.getExpression(), TerminalTokens.TokenNameDOT),
i
);
}
ArgumentListTreeImpl arguments = convertArguments(
firstTokenIn(e, TerminalTokens.TokenNameLPAREN),
e.arguments(),
lastTokenIn(e, TerminalTokens.TokenNameRPAREN)
);
MethodInvocationTreeImpl t = new MethodInvocationTreeImpl(
methodSelect,
convertTypeArguments(e.typeArguments()),
arguments
);
t.methodBinding = e.resolveConstructorBinding();
if (t.methodBinding != null) {
t.typeBinding = t.methodBinding.getDeclaringClass();
t.methodBinding = excludeRecovery(t.methodBinding, arguments.size());
}
i.binding = t.methodBinding;
usage(i.binding, i);
return new ExpressionStatementTreeImpl(
t,
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
private YieldStatementTreeImpl convertYield(YieldStatement e) {
InternalSyntaxToken yieldKeyword = null;
if (!e.isImplicit()) {
try {
yieldKeyword = firstTokenIn(e, TerminalTokens.TokenNameRestrictedIdentifierYield);
} catch (AssertionError | IndexOutOfBoundsException error) {
// TODO ECJ bug? should be "TerminalTokens.TokenNameRestrictedIdentifierYield" in all cases
yieldKeyword = firstTokenIn(e, TerminalTokens.TokenNameIdentifier);
}
}
return new YieldStatementTreeImpl(
yieldKeyword,
convertExpression(e.getExpression()),
lastTokenIn(e, TerminalTokens.TokenNameSEMICOLON)
);
}
private ExpressionTree convertExpression(Expression node) {
ExpressionTree t = createExpression(node);
((AbstractTypedTree) t).typeBinding = node.resolveTypeBinding();
return t;
}
private ExpressionTree createExpression(Expression node) {
switch (node.getNodeType()) {
case ASTNode.SIMPLE_NAME:
return convertSimpleName((SimpleName) node);
case ASTNode.QUALIFIED_NAME:
return convertQualifiedName((QualifiedName) node);
case ASTNode.FIELD_ACCESS:
return convertFieldAccess((FieldAccess) node);
case ASTNode.SUPER_FIELD_ACCESS:
return convertFieldAccess((SuperFieldAccess) node);
case ASTNode.THIS_EXPRESSION:
return convertThisExpression((ThisExpression) node);
case ASTNode.ARRAY_ACCESS:
return convertArrayAccess((ArrayAccess) node);
case ASTNode.ARRAY_CREATION:
return convertArrayCreation((ArrayCreation) node);
case ASTNode.ARRAY_INITIALIZER:
return convertArrayInitializer((ArrayInitializer) node);
case ASTNode.ASSIGNMENT:
return convertAssignment((Assignment) node);
case ASTNode.CAST_EXPRESSION:
return convertTypeCastExpression((CastExpression) node);
case ASTNode.CLASS_INSTANCE_CREATION:
return convertClassInstanceCreation((ClassInstanceCreation) node);
case ASTNode.CONDITIONAL_EXPRESSION:
return convertConditionalExpression((ConditionalExpression) node);
case ASTNode.INFIX_EXPRESSION:
return convertInfixExpression((InfixExpression) node);
case ASTNode.METHOD_INVOCATION:
return convertMethodInvocation((MethodInvocation) node);
case ASTNode.SUPER_METHOD_INVOCATION:
return convertMethodInvocation((SuperMethodInvocation) node);
case ASTNode.PARENTHESIZED_EXPRESSION:
return convertParenthesizedExpression((ParenthesizedExpression) node);
case ASTNode.POSTFIX_EXPRESSION:
return convertPostfixExpression((PostfixExpression) node);
case ASTNode.PREFIX_EXPRESSION:
return convertPrefixExpression((PrefixExpression) node);
case ASTNode.INSTANCEOF_EXPRESSION:
return convertInstanceOf((InstanceofExpression) node);
case ASTNode.PATTERN_INSTANCEOF_EXPRESSION:
return convertInstanceOf((PatternInstanceofExpression) node);
case ASTNode.LAMBDA_EXPRESSION:
return convertLambdaExpression((LambdaExpression) node);
case ASTNode.CREATION_REFERENCE:
return convertMethodReference((CreationReference) node);
case ASTNode.EXPRESSION_METHOD_REFERENCE:
return convertMethodReference((ExpressionMethodReference) node);
case ASTNode.TYPE_METHOD_REFERENCE:
return convertMethodReference((TypeMethodReference) node);
case ASTNode.SUPER_METHOD_REFERENCE:
return convertMethodReference((SuperMethodReference) node);
case ASTNode.SWITCH_EXPRESSION:
return convertSwitchExpression((SwitchExpression) node);
case ASTNode.TYPE_LITERAL:
return convertTypeLiteral((TypeLiteral) node);
case ASTNode.NULL_LITERAL:
return convertLiteral((NullLiteral) node);
case ASTNode.NUMBER_LITERAL:
return convertLiteral((NumberLiteral) node);
case ASTNode.CHARACTER_LITERAL:
return convertLiteral((CharacterLiteral) node);
case ASTNode.BOOLEAN_LITERAL:
return convertLiteral((BooleanLiteral) node);
case ASTNode.STRING_LITERAL:
return convertLiteral((StringLiteral) node);
case ASTNode.TEXT_BLOCK:
return convertTextBlock((TextBlock) node);
case ASTNode.NORMAL_ANNOTATION:
case ASTNode.MARKER_ANNOTATION:
case ASTNode.SINGLE_MEMBER_ANNOTATION:
return convertAnnotation((Annotation) node);
default:
throw new IllegalStateException(ASTNode.nodeClassForType(node.getNodeType()).toString());
}
}
private IdentifierTreeImpl convertSimpleName(SimpleName e) {
IdentifierTreeImpl t = createSimpleName(e);
usage(t.binding, t);
return t;
}
private MemberSelectExpressionTreeImpl convertQualifiedName(QualifiedName e) {
IdentifierTreeImpl rhs = createSimpleName(e.getName());
usage(rhs.binding, rhs);
return new MemberSelectExpressionTreeImpl(
convertExpression(e.getQualifier()),
firstTokenAfter(e.getQualifier(), TerminalTokens.TokenNameDOT),
rhs
);
}
private MemberSelectExpressionTreeImpl convertFieldAccess(FieldAccess e) {
IdentifierTreeImpl rhs = createSimpleName(e.getName());
usage(rhs.binding, rhs);
return new MemberSelectExpressionTreeImpl(
convertExpression(e.getExpression()),
firstTokenAfter(e.getExpression(), TerminalTokens.TokenNameDOT),
rhs
);
}
private MemberSelectExpressionTreeImpl convertFieldAccess(SuperFieldAccess e) {
IdentifierTreeImpl rhs = createSimpleName(e.getName());
usage(rhs.binding, rhs);
if (e.getQualifier() == null) {
// super.name
return new MemberSelectExpressionTreeImpl(
unqualifiedKeywordSuper(e),
firstTokenIn(e, TerminalTokens.TokenNameDOT),
rhs
);
}
// qualifier.super.name
AbstractTypedTree qualifier = (AbstractTypedTree) convertExpression(e.getQualifier());
KeywordSuper keywordSuper = new KeywordSuper(firstTokenAfter(e.getQualifier(), TerminalTokens.TokenNamesuper), null);
MemberSelectExpressionTreeImpl qualifiedSuper = new MemberSelectExpressionTreeImpl(
(ExpressionTree) qualifier,
firstTokenAfter(e.getQualifier(), TerminalTokens.TokenNameDOT),
keywordSuper
);
if (qualifier.typeBinding != null) {
keywordSuper.typeBinding = qualifier.typeBinding;
qualifiedSuper.typeBinding = keywordSuper.typeBinding.getSuperclass();
}
return new MemberSelectExpressionTreeImpl(
qualifiedSuper,
firstTokenBefore(e.getName(), TerminalTokens.TokenNameDOT),
rhs
);
}
private ExpressionTree convertThisExpression(ThisExpression e) {
if (e.getQualifier() == null) {
return new KeywordThis(firstTokenIn(e, TerminalTokens.TokenNamethis), null);
}
KeywordThis keywordThis = new KeywordThis(
firstTokenAfter(e.getQualifier(), TerminalTokens.TokenNamethis),
e.resolveTypeBinding()
);
return new MemberSelectExpressionTreeImpl(
convertExpression(e.getQualifier()),
firstTokenAfter(e.getQualifier(), TerminalTokens.TokenNameDOT),
keywordThis
);
}
private MemberSelectExpressionTreeImpl convertTypeLiteral(TypeLiteral e) {
return new MemberSelectExpressionTreeImpl(
(ExpressionTree) convertType(e.getType()),
lastTokenIn(e, TerminalTokens.TokenNameDOT),
new IdentifierTreeImpl(
lastTokenIn(e, TerminalTokens.TokenNameclass)
)
);
}
private ArrayAccessExpressionTreeImpl convertArrayAccess(ArrayAccess e) {
Expression index = e.getIndex();
return new ArrayAccessExpressionTreeImpl(
convertExpression(e.getArray()),
new ArrayDimensionTreeImpl(
firstTokenBefore(index, TerminalTokens.TokenNameLBRACKET),
convertExpression(index),
firstTokenAfter(index, TerminalTokens.TokenNameRBRACKET)
)
);
}
private NewArrayTreeImpl convertArrayCreation(ArrayCreation e) {
List dimensions = new ArrayList<>();
for (Object o : e.dimensions()) {
dimensions.add(new ArrayDimensionTreeImpl(
firstTokenBefore((Expression) o, TerminalTokens.TokenNameLBRACKET),
convertExpression((Expression) o),
firstTokenAfter((Expression) o, TerminalTokens.TokenNameRBRACKET)
));
}
InitializerListTreeImpl initializers = InitializerListTreeImpl.emptyList();
if (e.getInitializer() != null) {
assert dimensions.isEmpty();
TypeTree type = convertType(e.getType());
while (type.is(Tree.Kind.ARRAY_TYPE)) {
ArrayTypeTree arrayType = (ArrayTypeTree) type;
ArrayDimensionTreeImpl dimension = new ArrayDimensionTreeImpl(
arrayType.openBracketToken(),
null,
arrayType.closeBracketToken()
).completeAnnotations(arrayType.annotations());
dimensions.add(/* TODO suboptimal */ 0, dimension);
type = arrayType.type();
}
return ((NewArrayTreeImpl) convertExpression(e.getInitializer()))
.completeWithNewKeyword(firstTokenIn(e, TerminalTokens.TokenNamenew))
.complete(type)
.completeDimensions(dimensions);
}
TypeTree type = convertType(e.getType());
int index = dimensions.size() - 1;
while (type.is(Tree.Kind.ARRAY_TYPE)) {
if (!type.annotations().isEmpty()) {
((ArrayDimensionTreeImpl) dimensions.get(index))
.completeAnnotations(type.annotations());
}
index--;
type = ((ArrayTypeTree) type).type();
}
return new NewArrayTreeImpl(dimensions, initializers)
.complete(type)
.completeWithNewKeyword(firstTokenIn(e, TerminalTokens.TokenNamenew));
}
private NewArrayTreeImpl convertArrayInitializer(ArrayInitializer e) {
InitializerListTreeImpl initializers = InitializerListTreeImpl.emptyList();
for (int i = 0; i < e.expressions().size(); i++) {
Expression o = (Expression) e.expressions().get(i);
initializers.add(convertExpression(o));
final int commaTokenIndex = firstTokenIndexAfter(o);
if (tokenManager.get(commaTokenIndex).tokenType == TerminalTokens.TokenNameCOMMA) {
initializers.separators().add(firstTokenAfter(o, TerminalTokens.TokenNameCOMMA));
}
}
return new NewArrayTreeImpl(Collections.emptyList(),initializers)
.completeWithCurlyBraces(
firstTokenIn(e, TerminalTokens.TokenNameLBRACE),
lastTokenIn(e, TerminalTokens.TokenNameRBRACE)
);
}
private AssignmentExpressionTreeImpl convertAssignment(Assignment e) {
Op op = operators.get(e.getOperator());
return new AssignmentExpressionTreeImpl(
op.kind,
convertExpression(e.getLeftHandSide()),
firstTokenAfter(e.getLeftHandSide(), op.tokenType),
convertExpression(e.getRightHandSide())
);
}
private TypeCastExpressionTreeImpl convertTypeCastExpression(CastExpression e) {
Type type = e.getType();
if (type.getNodeType() == ASTNode.INTERSECTION_TYPE) {
List intersectionTypes = ((IntersectionType) type).types();
QualifiedIdentifierListTreeImpl bounds = QualifiedIdentifierListTreeImpl.emptyList();
for (int i = 1; i < intersectionTypes.size(); i++) {
Type o = (Type) intersectionTypes.get(i);
bounds.add(convertType(o));
if (i < intersectionTypes.size() - 1) {
bounds.separators().add(firstTokenAfter(o, TerminalTokens.TokenNameAND));
}
}
return new TypeCastExpressionTreeImpl(
firstTokenBefore(type, TerminalTokens.TokenNameLPAREN),
convertType((Type) intersectionTypes.get(0)),
firstTokenAfter((Type) intersectionTypes.get(0), TerminalTokens.TokenNameAND),
bounds,
firstTokenAfter(type, TerminalTokens.TokenNameRPAREN),
convertExpression(e.getExpression())
);
}
return new TypeCastExpressionTreeImpl(
firstTokenBefore(type, TerminalTokens.TokenNameLPAREN),
convertType(type),
firstTokenAfter(type, TerminalTokens.TokenNameRPAREN),
convertExpression(e.getExpression())
);
}
private NewClassTreeImpl convertClassInstanceCreation(ClassInstanceCreation e) {
ArgumentListTreeImpl arguments = convertArguments(
firstTokenAfter(e.getType(), TerminalTokens.TokenNameLPAREN),
e.arguments(),
firstTokenAfter(e.arguments().isEmpty() ? e.getType() : (ASTNode) e.arguments().get(e.arguments().size() - 1), TerminalTokens.TokenNameRPAREN)
);
ClassTreeImpl classBody = null;
if (e.getAnonymousClassDeclaration() != null) {
List members = new ArrayList<>();
for (Object o : e.getAnonymousClassDeclaration().bodyDeclarations()) {
processBodyDeclaration((BodyDeclaration) o, members);
}
classBody = new ClassTreeImpl(
Tree.Kind.CLASS,
firstTokenIn(e.getAnonymousClassDeclaration(), TerminalTokens.TokenNameLBRACE),
members,
lastTokenIn(e.getAnonymousClassDeclaration(), TerminalTokens.TokenNameRBRACE)
);
classBody.typeBinding = e.getAnonymousClassDeclaration().resolveBinding();
declaration(classBody.typeBinding, classBody);
}
NewClassTreeImpl t = new NewClassTreeImpl(
convertType(e.getType()),
arguments,
classBody
).completeWithNewKeyword(
e.getExpression() == null ? firstTokenIn(e, TerminalTokens.TokenNamenew) : firstTokenAfter(e.getExpression(), TerminalTokens.TokenNamenew)
).completeWithTypeArguments(
convertTypeArguments(e.typeArguments())
);
if (e.getExpression() != null) {
t.completeWithEnclosingExpression(convertExpression(e.getExpression()));
t.completeWithDotToken(firstTokenAfter(e.getExpression(), TerminalTokens.TokenNameDOT));
}
IdentifierTreeImpl i = (IdentifierTreeImpl) t.getConstructorIdentifier();
int nbArguments = arguments.size();
if (e.getAnonymousClassDeclaration() == null) {
i.binding = excludeRecovery(e.resolveConstructorBinding(), nbArguments);
} else {
i.binding = excludeRecovery(findConstructorForAnonymousClass(e.getAST(), i.typeBinding, e.resolveConstructorBinding()), nbArguments);
}
usage(i.binding, i);
return t;
}
private ConditionalExpressionTreeImpl convertConditionalExpression(ConditionalExpression e) {
return new ConditionalExpressionTreeImpl(
convertExpression(e.getExpression()),
firstTokenAfter(e.getExpression(), TerminalTokens.TokenNameQUESTION),
convertExpression(e.getThenExpression()),
firstTokenAfter(e.getThenExpression(), TerminalTokens.TokenNameCOLON),
convertExpression(e.getElseExpression())
);
}
private BinaryExpressionTreeImpl convertInfixExpression(InfixExpression e) {
Op op = operators.get(e.getOperator());
BinaryExpressionTreeImpl t = new BinaryExpressionTreeImpl(
op.kind,
convertExpression(e.getLeftOperand()),
firstTokenAfter(e.getLeftOperand(), op.tokenType),
convertExpression(e.getRightOperand())
);
for (Object o : e.extendedOperands()) {
Expression e2 = (Expression) o;
t.typeBinding = e.resolveTypeBinding();
t = new BinaryExpressionTreeImpl(
op.kind,
t,
firstTokenBefore(e2, op.tokenType),
convertExpression(e2)
);
}
return t;
}
private MethodInvocationTreeImpl convertMethodInvocation(MethodInvocation e) {
ArgumentListTreeImpl arguments = convertArguments(
firstTokenAfter(e.getName(), TerminalTokens.TokenNameLPAREN),
e.arguments(),
lastTokenIn(e, TerminalTokens.TokenNameRPAREN)
);
IdentifierTreeImpl rhs = createSimpleName(e.getName());
ExpressionTree memberSelect;
if (e.getExpression() == null) {
memberSelect = rhs;
} else {
memberSelect = new MemberSelectExpressionTreeImpl(
convertExpression(e.getExpression()),
firstTokenAfter(e.getExpression(), TerminalTokens.TokenNameDOT),
rhs
);
}
MethodInvocationTreeImpl t = new MethodInvocationTreeImpl(
memberSelect,
convertTypeArguments(e.typeArguments()),
arguments
);
t.methodBinding = excludeRecovery(e.resolveMethodBinding(), arguments.size());
rhs.binding = t.methodBinding;
usage(rhs.binding, rhs);
return t;
}
private MethodInvocationTreeImpl convertMethodInvocation(SuperMethodInvocation e) {
ArgumentListTreeImpl arguments = convertArguments(
firstTokenIn(e, TerminalTokens.TokenNameLPAREN),
e.arguments(),
lastTokenIn(e, TerminalTokens.TokenNameRPAREN)
);
IdentifierTreeImpl rhs = createSimpleName(e.getName());
ExpressionTree outermostSelect;
if (e.getQualifier() == null) {
outermostSelect = new MemberSelectExpressionTreeImpl(
unqualifiedKeywordSuper(e),
firstTokenIn(e, TerminalTokens.TokenNameDOT),
rhs
);
} else {
final int firstDotTokenIndex = tokenManager.firstIndexAfter(e.getQualifier(), TerminalTokens.TokenNameDOT);
AbstractTypedTree qualifier = (AbstractTypedTree) convertExpression(e.getQualifier());
KeywordSuper keywordSuper = new KeywordSuper(firstTokenAfter(e.getQualifier(), TerminalTokens.TokenNamesuper), null);
MemberSelectExpressionTreeImpl qualifiedSuper = new MemberSelectExpressionTreeImpl(
(ExpressionTree) qualifier,
createSyntaxToken(firstDotTokenIndex),
keywordSuper
);
if (qualifier.typeBinding != null) {
keywordSuper.typeBinding = qualifier.typeBinding;
qualifiedSuper.typeBinding = keywordSuper.typeBinding.getSuperclass();
}
outermostSelect = new MemberSelectExpressionTreeImpl(
qualifiedSuper,
createSyntaxToken(nextTokenIndex(firstDotTokenIndex, TerminalTokens.TokenNameDOT)),
rhs
);
}
MethodInvocationTreeImpl t = new MethodInvocationTreeImpl(
outermostSelect,
null,
arguments
);
t.methodBinding = excludeRecovery(e.resolveMethodBinding(), arguments.size());
rhs.binding = t.methodBinding;
usage(rhs.binding, rhs);
return t;
}
private ParenthesizedTreeImpl convertParenthesizedExpression(ParenthesizedExpression e) {
return new ParenthesizedTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNameLPAREN),
convertExpression(e.getExpression()),
firstTokenAfter(e.getExpression(), TerminalTokens.TokenNameRPAREN)
);
}
private InternalPostfixUnaryExpression convertPostfixExpression(PostfixExpression e) {
Op op = operators.get(e.getOperator());
return new InternalPostfixUnaryExpression(
op.kind,
convertExpression(e.getOperand()),
firstTokenAfter(e.getOperand(), op.tokenType)
);
}
private InternalPrefixUnaryExpression convertPrefixExpression(PrefixExpression e) {
Op op = operators.get(e.getOperator());
return new InternalPrefixUnaryExpression(
op.kind,
firstTokenIn(e, op.tokenType),
convertExpression(e.getOperand())
);
}
private InstanceOfTreeImpl convertInstanceOf(InstanceofExpression e) {
Expression leftOperand = e.getLeftOperand();
InternalSyntaxToken instanceofToken = firstTokenAfter(leftOperand, TerminalTokens.TokenNameinstanceof);
return new InstanceOfTreeImpl(convertExpression(leftOperand), instanceofToken, convertType(e.getRightOperand()));
}
private InstanceOfTreeImpl convertInstanceOf(PatternInstanceofExpression e) {
Expression leftOperand = e.getLeftOperand();
InternalSyntaxToken instanceofToken = firstTokenAfter(leftOperand, TerminalTokens.TokenNameinstanceof);
//FIXME future versions of ECJ are likely to return a Pattern rather than a SingleVariableDeclaration
return new InstanceOfTreeImpl(convertExpression(leftOperand), instanceofToken, new TypePatternTreeImpl(convertVariable(e.getRightOperand())));
}
private LambdaExpressionTreeImpl convertLambdaExpression(LambdaExpression e) {
List parameters = new ArrayList<>();
for (int i = 0; i < e.parameters().size(); i++) {
VariableDeclaration o = (VariableDeclaration) e.parameters().get(i);
VariableTreeImpl t;
if (o.getNodeType() == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
t = new VariableTreeImpl(createSimpleName(o.getName()));
IVariableBinding variableBinding = o.resolveBinding();
if (variableBinding != null) {
t.variableBinding = variableBinding;
((InferedTypeTree) t.type()).typeBinding = variableBinding.getType();
declaration(t.variableBinding, t);
}
} else {
t = convertVariable((SingleVariableDeclaration) o);
}
parameters.add(t);
if (i < e.parameters().size() - 1) {
t.setEndToken(firstTokenAfter(o, TerminalTokens.TokenNameCOMMA));
}
}
ASTNode body = e.getBody();
return new LambdaExpressionTreeImpl(
e.hasParentheses() ? firstTokenIn(e, TerminalTokens.TokenNameLPAREN) : null,
parameters,
e.hasParentheses() ? firstTokenBefore(body, TerminalTokens.TokenNameRPAREN) : null,
firstTokenBefore(body, TerminalTokens.TokenNameARROW),
body.getNodeType() == ASTNode.BLOCK ? convertBlock((Block) body) : convertExpression((Expression) body)
);
}
private MethodReferenceTreeImpl convertMethodReference(CreationReference e) {
MethodReferenceTreeImpl t = new MethodReferenceTreeImpl(
convertType(e.getType()),
lastTokenIn(e, TerminalTokens.TokenNameCOLON_COLON)
);
IdentifierTreeImpl i = new IdentifierTreeImpl(lastTokenIn(e, TerminalTokens.TokenNamenew));
i.binding = e.resolveMethodBinding();
usage(i.binding, i);
t.complete(convertTypeArguments(e.typeArguments()), i);
return t;
}
private MethodReferenceTreeImpl convertMethodReference(ExpressionMethodReference e) {
MethodReferenceTreeImpl t = new MethodReferenceTreeImpl(
convertExpression(e.getExpression()),
firstTokenAfter(e.getExpression(), TerminalTokens.TokenNameCOLON_COLON)
);
IdentifierTreeImpl i = createSimpleName(e.getName());
usage(i.binding, i);
t.complete(convertTypeArguments(e.typeArguments()), i);
return t;
}
private MethodReferenceTreeImpl convertMethodReference(TypeMethodReference e) {
MethodReferenceTreeImpl t = new MethodReferenceTreeImpl(
convertType(e.getType()),
firstTokenAfter(e.getType(), TerminalTokens.TokenNameCOLON_COLON)
);
IdentifierTreeImpl i = createSimpleName(e.getName());
usage(i.binding, i);
t.complete(convertTypeArguments(e.typeArguments()), i);
return t;
}
private MethodReferenceTreeImpl convertMethodReference(SuperMethodReference e) {
MethodReferenceTreeImpl t;
if (e.getQualifier() != null) {
t = new MethodReferenceTreeImpl(
new MemberSelectExpressionTreeImpl(
convertExpression(e.getQualifier()),
firstTokenAfter(e.getQualifier(), TerminalTokens.TokenNameDOT),
unqualifiedKeywordSuper(e)
),
firstTokenAfter(e.getQualifier(), TerminalTokens.TokenNameCOLON_COLON)
);
} else {
t = new MethodReferenceTreeImpl(
unqualifiedKeywordSuper(e),
firstTokenIn(e, TerminalTokens.TokenNameCOLON_COLON)
);
}
IdentifierTreeImpl i = createSimpleName(e.getName());
usage(i.binding, i);
t.complete(convertTypeArguments(e.typeArguments()), i);
return t;
}
private SwitchExpressionTreeImpl convertSwitchExpression(SwitchExpression e) {
Expression expr = e.getExpression();
return new SwitchExpressionTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNameswitch),
firstTokenIn(e, TerminalTokens.TokenNameLPAREN),
convertExpression(expr),
firstTokenAfter(expr, TerminalTokens.TokenNameRPAREN),
firstTokenAfter(expr, TerminalTokens.TokenNameLBRACE),
convertSwitchStatements(e.statements()),
lastTokenIn(e, TerminalTokens.TokenNameRBRACE)
);
}
private LiteralTreeImpl convertLiteral(NullLiteral e) {
return new LiteralTreeImpl(Tree.Kind.NULL_LITERAL, firstTokenIn(e, TerminalTokens.TokenNamenull));
}
private ExpressionTree convertLiteral(NumberLiteral e) {
int tokenIndex = tokenManager.findIndex(e.getStartPosition(), ANY_TOKEN, true);
int tokenType = tokenManager.get(tokenIndex).tokenType;
boolean unaryMinus = tokenType == TerminalTokens.TokenNameMINUS;
if (unaryMinus) {
tokenIndex++;
tokenType = tokenManager.get(tokenIndex).tokenType;
}
ExpressionTree result;
switch (tokenType) {
case TerminalTokens.TokenNameIntegerLiteral:
result = new LiteralTreeImpl(Tree.Kind.INT_LITERAL, createSyntaxToken(tokenIndex));
break;
case TerminalTokens.TokenNameLongLiteral:
result = new LiteralTreeImpl(Tree.Kind.LONG_LITERAL, createSyntaxToken(tokenIndex));
break;
case TerminalTokens.TokenNameFloatingPointLiteral:
result = new LiteralTreeImpl(Tree.Kind.FLOAT_LITERAL, createSyntaxToken(tokenIndex));
break;
case TerminalTokens.TokenNameDoubleLiteral:
result = new LiteralTreeImpl(Tree.Kind.DOUBLE_LITERAL, createSyntaxToken(tokenIndex));
break;
default:
throw new IllegalStateException();
}
((LiteralTreeImpl) result).typeBinding = e.resolveTypeBinding();
if (unaryMinus) {
return new InternalPrefixUnaryExpression(Tree.Kind.UNARY_MINUS, createSyntaxToken(tokenIndex - 1), result);
}
return result;
}
private LiteralTreeImpl convertLiteral(CharacterLiteral e) {
return new LiteralTreeImpl(Tree.Kind.CHAR_LITERAL, firstTokenIn(e, TerminalTokens.TokenNameCharacterLiteral));
}
private LiteralTreeImpl convertLiteral(BooleanLiteral e) {
InternalSyntaxToken value = firstTokenIn(e, e.booleanValue() ? TerminalTokens.TokenNametrue : TerminalTokens.TokenNamefalse);
return new LiteralTreeImpl(Tree.Kind.BOOLEAN_LITERAL, value);
}
private LiteralTreeImpl convertLiteral(StringLiteral e) {
return new LiteralTreeImpl(Tree.Kind.STRING_LITERAL, firstTokenIn(e, TerminalTokens.TokenNameStringLiteral));
}
private LiteralTreeImpl convertTextBlock(TextBlock e) {
return new LiteralTreeImpl(Tree.Kind.TEXT_BLOCK, firstTokenIn(e, TerminalTokens.TokenNameTextBlock));
}
private AnnotationTreeImpl convertAnnotation(Annotation e) {
ArgumentListTreeImpl arguments = ArgumentListTreeImpl.emptyList();
if (e.getNodeType() == ASTNode.SINGLE_MEMBER_ANNOTATION) {
arguments.add(convertExpression(((SingleMemberAnnotation) e).getValue()));
arguments.complete(
firstTokenIn(e, TerminalTokens.TokenNameLPAREN),
lastTokenIn(e, TerminalTokens.TokenNameRPAREN)
);
} else if (e.getNodeType() == ASTNode.NORMAL_ANNOTATION) {
for (int i = 0; i < ((NormalAnnotation) e).values().size(); i++) {
MemberValuePair o = (MemberValuePair) ((NormalAnnotation) e).values().get(i);
arguments.add(new AssignmentExpressionTreeImpl(
Tree.Kind.ASSIGNMENT,
createSimpleName(o.getName()),
firstTokenAfter(o.getName(), TerminalTokens.TokenNameEQUAL),
convertExpression(o.getValue())
));
if (i < ((NormalAnnotation) e).values().size() - 1) {
arguments.separators().add(firstTokenAfter(o, TerminalTokens.TokenNameCOMMA));
}
}
arguments.complete(
firstTokenIn(e, TerminalTokens.TokenNameLPAREN),
lastTokenIn(e, TerminalTokens.TokenNameRPAREN)
);
}
return new AnnotationTreeImpl(
firstTokenIn(e, TerminalTokens.TokenNameAT),
(TypeTree) convertExpression(e.getTypeName()),
arguments
);
}
private KeywordSuper unqualifiedKeywordSuper(ASTNode node) {
InternalSyntaxToken token = firstTokenIn(node, TerminalTokens.TokenNamesuper);
do {
if (node instanceof AbstractTypeDeclaration) {
return new KeywordSuper(token, ((AbstractTypeDeclaration) node).resolveBinding());
}
if (node instanceof AnonymousClassDeclaration) {
return new KeywordSuper(token, ((AnonymousClassDeclaration) node).resolveBinding());
}
node = node.getParent();
} while (true);
}
private TypeTree convertType(Type node) {
switch (node.getNodeType()) {
case ASTNode.PRIMITIVE_TYPE:
return convertPrimitiveType((PrimitiveType) node);
case ASTNode.SIMPLE_TYPE:
return convertSimpleType((SimpleType) node);
case ASTNode.UNION_TYPE:
return convertUnionType((UnionType) node);
case ASTNode.ARRAY_TYPE:
return convertArrayType((ArrayType) node);
case ASTNode.PARAMETERIZED_TYPE:
return convertParameterizedType((ParameterizedType) node);
case ASTNode.QUALIFIED_TYPE:
return convertQualifiedType((QualifiedType) node);
case ASTNode.NAME_QUALIFIED_TYPE:
return convertNamedQualifiedType((NameQualifiedType) node);
case ASTNode.WILDCARD_TYPE:
return convertWildcardType((WildcardType) node);
default:
throw new IllegalStateException(ASTNode.nodeClassForType(node.getNodeType()).toString());
}
}
private JavaTree.PrimitiveTypeTreeImpl convertPrimitiveType(PrimitiveType e) {
final JavaTree.PrimitiveTypeTreeImpl t;
switch (e.getPrimitiveTypeCode().toString()) {
case "byte":
t = new JavaTree.PrimitiveTypeTreeImpl(lastTokenIn(e, TerminalTokens.TokenNamebyte));
break;
case "short":
t = new JavaTree.PrimitiveTypeTreeImpl(lastTokenIn(e, TerminalTokens.TokenNameshort));
break;
case "char":
t = new JavaTree.PrimitiveTypeTreeImpl(lastTokenIn(e, TerminalTokens.TokenNamechar));
break;
case "int":
t = new JavaTree.PrimitiveTypeTreeImpl(lastTokenIn(e, TerminalTokens.TokenNameint));
break;
case "long":
t = new JavaTree.PrimitiveTypeTreeImpl(lastTokenIn(e, TerminalTokens.TokenNamelong));
break;
case "float":
t = new JavaTree.PrimitiveTypeTreeImpl(lastTokenIn(e, TerminalTokens.TokenNamefloat));
break;
case "double":
t = new JavaTree.PrimitiveTypeTreeImpl(lastTokenIn(e, TerminalTokens.TokenNamedouble));
break;
case "boolean":
t = new JavaTree.PrimitiveTypeTreeImpl(lastTokenIn(e, TerminalTokens.TokenNameboolean));
break;
case "void":
t = new JavaTree.PrimitiveTypeTreeImpl(lastTokenIn(e, TerminalTokens.TokenNamevoid));
break;
default:
throw new IllegalStateException(e.getPrimitiveTypeCode().toString());
}
t.complete(convertAnnotations(e.annotations()));
t.typeBinding = e.resolveBinding();
return t;
}
private JavaTree.AnnotatedTypeTree convertSimpleType(SimpleType e) {
List annotations = new ArrayList<>();
for (Object o : e.annotations()) {
annotations.add((AnnotationTree) convertExpression(((Annotation) o)));
}
JavaTree.AnnotatedTypeTree t = e.isVar() ? convertVarType(e) : (JavaTree.AnnotatedTypeTree) convertExpression(e.getName());
t.complete(annotations);
// typeBinding is assigned by convertVarType or convertExpression
return t;
}
private JavaTree.UnionTypeTreeImpl convertUnionType(UnionType e) {
QualifiedIdentifierListTreeImpl alternatives = QualifiedIdentifierListTreeImpl.emptyList();
for (int i = 0; i < e.types().size(); i++) {
Type o = (Type) e.types().get(i);
alternatives.add(convertType(o));
if (i < e.types().size() - 1) {
alternatives.separators().add(firstTokenAfter(o, TerminalTokens.TokenNameOR));
}
}
JavaTree.UnionTypeTreeImpl t = new JavaTree.UnionTypeTreeImpl(alternatives);
t.typeBinding = e.resolveBinding();
return t;
}
private TypeTree convertArrayType(ArrayType e) {
@Nullable ITypeBinding elementTypeBinding = e.getElementType().resolveBinding();
TypeTree t = convertType(e.getElementType());
int tokenIndex = tokenManager.firstIndexAfter(e.getElementType(), TerminalTokens.TokenNameLBRACKET);
for (int i = 0; i < e.dimensions().size(); i++) {
if (i > 0) {
tokenIndex = nextTokenIndex(tokenIndex, TerminalTokens.TokenNameLBRACKET);
}
t = new JavaTree.ArrayTypeTreeImpl(
t,
(List) convertAnnotations(((Dimension) e.dimensions().get(i)).annotations()),
createSyntaxToken(tokenIndex),
createSyntaxToken(nextTokenIndex(tokenIndex, TerminalTokens.TokenNameRBRACKET))
);
if (elementTypeBinding != null) {
((JavaTree.ArrayTypeTreeImpl) t).typeBinding = elementTypeBinding.createArrayType(i + 1);
}
}
return t;
}
private JavaTree.ParameterizedTypeTreeImpl convertParameterizedType(ParameterizedType e) {
int pos = e.getStartPosition() + e.getLength() - 1;
JavaTree.ParameterizedTypeTreeImpl t = new JavaTree.ParameterizedTypeTreeImpl(
convertType(e.getType()),
convertTypeArguments(
firstTokenAfter(e.getType(), TerminalTokens.TokenNameLESS),
e.typeArguments(),
new InternalSyntaxToken(
compilationUnit.getLineNumber(pos),
compilationUnit.getColumnNumber(pos),
">",
/* TODO */ Collections.emptyList(),
false
)
)
);
t.typeBinding = e.resolveBinding();
return t;
}
private MemberSelectExpressionTreeImpl convertQualifiedType(QualifiedType e) {
MemberSelectExpressionTreeImpl t = new MemberSelectExpressionTreeImpl(
(ExpressionTree) convertType(e.getQualifier()),
firstTokenAfter(e.getQualifier(), TerminalTokens.TokenNameDOT),
createSimpleName(e.getName())
);
((IdentifierTreeImpl) t.identifier()).complete(convertAnnotations(e.annotations()));
t.typeBinding = e.resolveBinding();
return t;
}
private MemberSelectExpressionTreeImpl convertNamedQualifiedType(NameQualifiedType e) {
MemberSelectExpressionTreeImpl t = new MemberSelectExpressionTreeImpl(
convertExpression(e.getQualifier()),
firstTokenAfter(e.getQualifier(), TerminalTokens.TokenNameDOT),
createSimpleName(e.getName())
);
((IdentifierTreeImpl) t.identifier()).complete(convertAnnotations(e.annotations()));
t.typeBinding = e.resolveBinding();
return t;
}
private JavaTree.WildcardTreeImpl convertWildcardType(WildcardType e) {
final InternalSyntaxToken questionToken = e.annotations().isEmpty()
? firstTokenIn(e, TerminalTokens.TokenNameQUESTION)
: firstTokenAfter((ASTNode) e.annotations().get(e.annotations().size() - 1), TerminalTokens.TokenNameQUESTION);
JavaTree.WildcardTreeImpl t;
Type bound = e.getBound();
if (bound == null) {
t = new JavaTree.WildcardTreeImpl(questionToken);
} else {
t = new JavaTree.WildcardTreeImpl(
e.isUpperBound() ? Tree.Kind.EXTENDS_WILDCARD : Tree.Kind.SUPER_WILDCARD,
e.isUpperBound() ? firstTokenBefore(bound, TerminalTokens.TokenNameextends) : firstTokenBefore(bound, TerminalTokens.TokenNamesuper),
convertType(bound)
).complete(questionToken);
}
t.complete(convertAnnotations(e.annotations()));
t.typeBinding = e.resolveBinding();
return t;
}
@Nullable
private static IMethodBinding excludeRecovery(@Nullable IMethodBinding methodBinding, int arguments) {
if (methodBinding == null) {
return null;
}
if (methodBinding.isVarargs()) {
if (arguments + 1 < methodBinding.getParameterTypes().length) {
return null;
}
} else {
if (arguments != methodBinding.getParameterTypes().length) {
return null;
}
}
return methodBinding;
}
@Nullable
private static IMethodBinding findConstructorForAnonymousClass(AST ast, @Nullable ITypeBinding typeBinding, @Nullable IMethodBinding methodBinding) {
if (typeBinding == null || methodBinding == null) {
return null;
}
if (typeBinding.isInterface()) {
typeBinding = ast.resolveWellKnownType("java.lang.Object");
}
for (IMethodBinding m : typeBinding.getDeclaredMethods()) {
if (methodBinding.isSubsignature(m)) {
return m;
}
}
return null;
}
private List convertAnnotations(List> e) {
List annotations = new ArrayList<>();
for (Object o : e) {
annotations.add((AnnotationTree) convertExpression(
((Annotation) o)
));
}
return annotations;
}
private ModifiersTreeImpl convertModifiers(List> source) {
List modifiers = new ArrayList<>();
for (Object o : source) {
modifiers.add(convertModifier((IExtendedModifier) o));
}
return new ModifiersTreeImpl(modifiers);
}
private ModifierTree convertModifier(IExtendedModifier node) {
switch (((ASTNode) node).getNodeType()) {
case ASTNode.NORMAL_ANNOTATION:
case ASTNode.MARKER_ANNOTATION:
case ASTNode.SINGLE_MEMBER_ANNOTATION:
return (AnnotationTree) convertExpression((Expression) node);
case ASTNode.MODIFIER:
return convertModifier((org.eclipse.jdt.core.dom.Modifier) node);
default:
throw new IllegalStateException(ASTNode.nodeClassForType(((ASTNode) node).getNodeType()).toString());
}
}
private ModifierTree convertModifier(org.eclipse.jdt.core.dom.Modifier node) {
switch (node.getKeyword().toString()) {
case "public":
return new ModifierKeywordTreeImpl(Modifier.PUBLIC, firstTokenIn(node, TerminalTokens.TokenNamepublic));
case "protected":
return new ModifierKeywordTreeImpl(Modifier.PROTECTED, firstTokenIn(node, TerminalTokens.TokenNameprotected));
case "private":
return new ModifierKeywordTreeImpl(Modifier.PRIVATE, firstTokenIn(node, TerminalTokens.TokenNameprivate));
case "static":
return new ModifierKeywordTreeImpl(Modifier.STATIC, firstTokenIn(node, TerminalTokens.TokenNamestatic));
case "abstract":
return new ModifierKeywordTreeImpl(Modifier.ABSTRACT, firstTokenIn(node, TerminalTokens.TokenNameabstract));
case "final":
return new ModifierKeywordTreeImpl(Modifier.FINAL, firstTokenIn(node, TerminalTokens.TokenNamefinal));
case "native":
return new ModifierKeywordTreeImpl(Modifier.NATIVE, firstTokenIn(node, TerminalTokens.TokenNamenative));
case "synchronized":
return new ModifierKeywordTreeImpl(Modifier.SYNCHRONIZED, firstTokenIn(node, TerminalTokens.TokenNamesynchronized));
case "transient":
return new ModifierKeywordTreeImpl(Modifier.TRANSIENT, firstTokenIn(node, TerminalTokens.TokenNametransient));
case "volatile":
return new ModifierKeywordTreeImpl(Modifier.VOLATILE, firstTokenIn(node, TerminalTokens.TokenNamevolatile));
case "strictfp":
return new ModifierKeywordTreeImpl(Modifier.STRICTFP, firstTokenIn(node, TerminalTokens.TokenNamestrictfp));
case "default":
return new ModifierKeywordTreeImpl(Modifier.DEFAULT, firstTokenIn(node, TerminalTokens.TokenNamedefault));
case "sealed":
return new ModifierKeywordTreeImpl(Modifier.SEALED, firstTokenIn(node, TerminalTokens.TokenNameRestrictedIdentifiersealed));
case "non-sealed": {
return new ModifierKeywordTreeImpl(Modifier.NON_SEALED, firstTokenIn(node, TerminalTokens.TokenNamenon_sealed));
}
default:
throw new IllegalStateException(node.getKeyword().toString());
}
}
private static final int ANY_TOKEN = -1;
private static final Map
© 2015 - 2025 Weber Informatics LLC | Privacy Policy