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

org.sonar.java.model.JavaTree 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 java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.EnumMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.sonar.java.Preconditions;
import org.sonar.java.annotations.Beta;
import org.sonar.java.ast.parser.QualifiedIdentifierListTreeImpl;
import org.sonarsource.analyzer.commons.collections.ListUtils;
import org.sonar.java.model.declaration.AnnotationTreeImpl;
import org.sonar.java.model.expression.AssessableExpressionTree;
import org.sonar.java.model.expression.TypeArgumentListTreeImpl;
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.tree.AnnotationTree;
import org.sonar.plugins.java.api.tree.ArrayTypeTree;
import org.sonar.plugins.java.api.tree.CompilationUnitTree;
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.ImportTree;
import org.sonar.plugins.java.api.tree.ListTree;
import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree;
import org.sonar.plugins.java.api.tree.ModuleDeclarationTree;
import org.sonar.plugins.java.api.tree.PackageDeclarationTree;
import org.sonar.plugins.java.api.tree.ParameterizedTypeTree;
import org.sonar.plugins.java.api.tree.PrimitiveTypeTree;
import org.sonar.plugins.java.api.tree.SyntaxToken;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.TreeVisitor;
import org.sonar.plugins.java.api.tree.TypeArguments;
import org.sonar.plugins.java.api.tree.TypeTree;
import org.sonar.plugins.java.api.tree.UnionTypeTree;
import org.sonar.plugins.java.api.tree.WildcardTree;

public abstract class JavaTree implements Tree {

  protected CompilationUnitTreeImpl root;

  @Nullable
  private Tree parent;

  private List children;


  @Override
  @Nullable
  public SyntaxToken firstToken() {
    for (Tree child : getChildren()) {
      SyntaxToken first = child.firstToken();
      if (first != null) {
        return first;
      }
    }
    return null;
  }

  @Override
  @Nullable
  public SyntaxToken lastToken() {
    List trees = getChildren();
    for (int index = trees.size() - 1; index >= 0; index--) {
      SyntaxToken last = trees.get(index).lastToken();
      if (last != null) {
        return last;
      }
    }
    return null;
  }

  public int getLine() {
    SyntaxToken firstSyntaxToken = firstToken();
    if (firstSyntaxToken == null) {
      return -1;
    }
    return LineUtils.startLine(firstSyntaxToken);
  }

  @Override
  public final boolean is(Kind... kinds) {
    Kind treeKind = kind();
    for (Kind kindIter : kinds) {
      if (treeKind == kindIter) {
        return true;
      }
    }
    return false;
  }

  @Override
  public Tree parent() {
    return parent;
  }

  public void setParent(Tree parent) {
    this.root = ((JavaTree) parent).root;
    this.parent = parent;
  }

  /**
   * Creates iterable for children of this node.
   * Note that iterable may contain {@code null} elements.
   *
   * @throws java.lang.UnsupportedOperationException if {@link #isLeaf()} returns {@code true}
   */
  protected abstract List children();

  public List getChildren() {
    if(children == null) {
      children = children().stream()
        .filter(Objects::nonNull)
        .collect(Collectors.toList());
    }
    return children;
  }

  public boolean isLeaf() {
    return false;
  }

  public static class CompilationUnitTreeImpl extends JavaTree implements CompilationUnitTree {
    @Nullable
    private final PackageDeclarationTree packageDeclaration;
    private final List imports;
    private final List types;
    @Nullable
    private final ModuleDeclarationTree moduleDeclaration;
    private final SyntaxToken eofToken;
    public JSema sema;

    private final Map> warnings = new EnumMap<>(JProblem.Type.class);

    public CompilationUnitTreeImpl(@Nullable PackageDeclarationTree packageDeclaration, List imports, List types,
      @Nullable ModuleDeclarationTree moduleDeclaration, SyntaxToken eofToken) {
      this.root = this;
      this.packageDeclaration = packageDeclaration;
      this.imports = imports;
      this.types = types;
      this.moduleDeclaration = moduleDeclaration;
      this.eofToken = eofToken;
    }

    @Override
    public Kind kind() {
      return Kind.COMPILATION_UNIT;
    }

    @Override
    public List imports() {
      return imports;
    }

    @Override
    public List types() {
      return types;
    }

    @Beta
    public List warnings(JProblem.Type type) {
      return Collections.unmodifiableList(new ArrayList<>(warnings.getOrDefault(type, Collections.emptySet())));
    }

    @Override
    public void accept(TreeVisitor visitor) {
      visitor.visitCompilationUnit(this);
    }

    @Override
    public List children() {
      Iterable packageIterator = packageDeclaration == null ? Collections.emptyList() : Collections.singletonList(packageDeclaration);
      Iterable moduleIterator = moduleDeclaration == null ? Collections.emptyList() : Collections.singletonList(moduleDeclaration);
      return ListUtils.concat(
        packageIterator,
        imports,
        types,
        moduleIterator,
        Collections.singletonList(eofToken));
    }

    @Nullable
    @Override
    public PackageDeclarationTree packageDeclaration() {
      return packageDeclaration;
    }

    @Nullable
    @Override
    public ModuleDeclarationTree moduleDeclaration() {
      return moduleDeclaration;
    }

    @Override
    public SyntaxToken eofToken() {
      return eofToken;
    }

    public void addWarnings(Map> warnings) {
      this.warnings.putAll(warnings);
    }

  }

  public static class PackageDeclarationTreeImpl extends JavaTree implements PackageDeclarationTree {

    private final List annotations;
    private final SyntaxToken packageKeyword;
    private final ExpressionTree packageName;
    private final SyntaxToken semicolonToken;

    public PackageDeclarationTreeImpl(List annotations, SyntaxToken packageKeyword, ExpressionTree packageName, SyntaxToken semicolonToken) {
      this.annotations = Objects.requireNonNull(annotations);
      this.packageKeyword = packageKeyword;
      this.packageName = packageName;
      this.semicolonToken = semicolonToken;
    }

    @Override
    public void accept(TreeVisitor visitor) {
      visitor.visitPackage(this);
    }

    @Override
    public List annotations() {
      return annotations;
    }

    @Override
    public SyntaxToken packageKeyword() {
      return packageKeyword;
    }

    @Override
    public ExpressionTree packageName() {
      return packageName;
    }

    @Override
    public SyntaxToken semicolonToken() {
      return semicolonToken;
    }

    @Override
    public Kind kind() {
      return Tree.Kind.PACKAGE;
    }

    @Override
    public List children() {
      return ListUtils.concat(
        annotations,
        Arrays.asList(packageKeyword, packageName, semicolonToken)
        );
    }

    public static String packageNameAsString(@Nullable PackageDeclarationTree tree) {
      if (tree == null) {
        return "";
      }
      Deque pieces = new LinkedList<>();
      ExpressionTree expr = tree.packageName();
      while (expr.is(Tree.Kind.MEMBER_SELECT)) {
        MemberSelectExpressionTree mse = (MemberSelectExpressionTree) expr;
        pieces.push(mse.identifier().name());
        pieces.push(mse.operatorToken().text());
        expr = mse.expression();
      }
      if (expr.is(Tree.Kind.IDENTIFIER)) {
        IdentifierTree idt = (IdentifierTree) expr;
        pieces.push(idt.name());
      }

      StringBuilder sb = new StringBuilder();
      for (String piece : pieces) {
        sb.append(piece);
      }
      return sb.toString();
    }
  }

  public static class ImportTreeImpl extends JavaTree implements ImportTree {
    private final boolean isStatic;
    private final Tree qualifiedIdentifier;
    private final SyntaxToken semicolonToken;
    private final SyntaxToken importToken;
    private final SyntaxToken staticToken;

    public IBinding binding;

    public ImportTreeImpl(InternalSyntaxToken importToken, @Nullable InternalSyntaxToken staticToken,
                          Tree qualifiedIdentifier, InternalSyntaxToken semiColonToken) {
      this.importToken = importToken;
      this.staticToken = staticToken;
      this.qualifiedIdentifier = qualifiedIdentifier;
      this.semicolonToken = semiColonToken;
      isStatic = staticToken != null;
    }

    @Nullable
    public Symbol symbol() {
      if (binding != null) {
        switch (binding.getKind()) {
          case IBinding.TYPE:
            return root.sema.typeSymbol((ITypeBinding) binding);
          case IBinding.METHOD:
            return root.sema.methodSymbol((IMethodBinding) binding);
          case IBinding.VARIABLE:
            return root.sema.variableSymbol((IVariableBinding) binding);
        }
      }
      return null;
    }

    @Override
    public Kind kind() {
      return Kind.IMPORT;
    }

    @Override
    public boolean isStatic() {
      return isStatic;
    }

    @Override
    public SyntaxToken importKeyword() {
      return importToken;
    }

    @Nullable
    @Override
    public SyntaxToken staticKeyword() {
      return staticToken;
    }

    @Override
    public Tree qualifiedIdentifier() {
      return qualifiedIdentifier;
    }

    @Override
    public SyntaxToken semicolonToken() {
      return semicolonToken;
    }

    @Override
    public void accept(TreeVisitor visitor) {
      visitor.visitImport(this);
    }

    @Override
    public List children() {
      return ListUtils.concat(
        Collections.singletonList(importToken),
        isStatic ? Collections.singletonList(staticToken) : Collections.emptyList(),
        Arrays.asList(qualifiedIdentifier, semicolonToken));
    }
  }

  public static class WildcardTreeImpl extends AbstractTypedTree implements WildcardTree, AnnotatedTypeTree {

    private SyntaxToken queryToken;
    @Nullable
    private final SyntaxToken extendsOrSuperToken;
    private final Kind kind;
    @Nullable
    private final TypeTree bound;
    private List annotations;

    public WildcardTreeImpl(InternalSyntaxToken queryToken) {
      this.kind = Kind.UNBOUNDED_WILDCARD;
      this.annotations = Collections.emptyList();
      this.queryToken = queryToken;
      this.extendsOrSuperToken = null;
      this.bound = null;
    }

    public WildcardTreeImpl(Kind kind, InternalSyntaxToken extendsOrSuperToken, TypeTree bound) {
      Preconditions.checkState(kind == Kind.EXTENDS_WILDCARD || kind == Kind.SUPER_WILDCARD);
      this.kind = kind;
      this.annotations = Collections.emptyList();
      this.extendsOrSuperToken = extendsOrSuperToken;
      this.bound = bound;
    }

    public WildcardTreeImpl complete(InternalSyntaxToken queryToken) {
      Preconditions.checkState(kind == Kind.EXTENDS_WILDCARD || kind == Kind.SUPER_WILDCARD);
      this.queryToken = queryToken;
      return this;
    }

    @Override
    public void complete(List annotations) {
      this.annotations = annotations;
    }

    @Override
    public Kind kind() {
      return kind;
    }

    @Override
    public List annotations() {
      return annotations;
    }

    @Override
    public SyntaxToken queryToken() {
      return queryToken;
    }

    @Nullable
    @Override
    public SyntaxToken extendsOrSuperToken() {
      return extendsOrSuperToken;
    }

    @Nullable
    @Override
    public TypeTree bound() {
      return bound;
    }

    @Override
    public void accept(TreeVisitor visitor) {
      visitor.visitWildcard(this);
    }

    @Override
    public List children() {
      List builder = new ArrayList<>(annotations);
      builder.add(queryToken);
      if (bound != null) {
        builder.add(extendsOrSuperToken);
        builder.add(bound);
      }
      return Collections.unmodifiableList(builder);
    }
  }

  public static class UnionTypeTreeImpl extends AbstractTypedTree implements UnionTypeTree {
    private final ListTree typeAlternatives;

    public UnionTypeTreeImpl(QualifiedIdentifierListTreeImpl typeAlternatives) {
      this.typeAlternatives = Objects.requireNonNull(typeAlternatives);
    }

    @Override
    public Kind kind() {
      return Kind.UNION_TYPE;
    }

    @Override
    public ListTree typeAlternatives() {
      return typeAlternatives;
    }

    @Override
    public void accept(TreeVisitor visitor) {
      visitor.visitUnionType(this);
    }

    @Override
    public List children() {
      return Collections.singletonList(typeAlternatives);
    }

    @Override
    public List annotations() {
      return Collections.emptyList();
    }
  }

  public static class NotImplementedTreeImpl extends AssessableExpressionTree {

    public NotImplementedTreeImpl() {
    }

    @Override
    public Kind kind() {
      return Kind.OTHER;
    }

    @Override
    public void accept(TreeVisitor visitor) {
      visitor.visitOther(this);
    }

    @Override
    public boolean isLeaf() {
      return true;
    }

    @Override
    public List children() {
      throw new UnsupportedOperationException();
    }
  }

  public static class PrimitiveTypeTreeImpl extends AssessableExpressionTree implements PrimitiveTypeTree, AnnotatedTypeTree {

    private final InternalSyntaxToken token;
    private List annotations;

    public PrimitiveTypeTreeImpl(InternalSyntaxToken token) {
      this.token = token;
      this.annotations = Collections.emptyList();
    }

    @Override
    public void complete(List annotations) {
      this.annotations = annotations;
    }

    @Override
    public Kind kind() {
      return Kind.PRIMITIVE_TYPE;
    }

    @Override
    public void accept(TreeVisitor visitor) {
      visitor.visitPrimitiveType(this);
    }

    @Override
    public SyntaxToken keyword() {
      return token;
    }

    @Override
    public List children() {
      return ListUtils.concat(annotations, Collections.singletonList(token));
    }

    @Override
    public List annotations() {
      return annotations;
    }
  }

  public static class ParameterizedTypeTreeImpl extends AssessableExpressionTree implements ParameterizedTypeTree, AnnotatedTypeTree {

    private final TypeTree type;
    private final TypeArguments typeArguments;
    private List annotations;

    public ParameterizedTypeTreeImpl(TypeTree type, TypeArgumentListTreeImpl typeArguments) {
      this.type = Objects.requireNonNull(type);
      this.typeArguments = Objects.requireNonNull(typeArguments);
      this.annotations = Collections.emptyList();
    }

    @Override
    public void complete(List annotations) {
      this.annotations = annotations;
    }

    @Override
    public Kind kind() {
      return Kind.PARAMETERIZED_TYPE;
    }

    @Override
    public TypeTree type() {
      return type;
    }

    @Override
    public TypeArguments typeArguments() {
      return typeArguments;
    }

    @Override
    public List annotations() {
      return annotations;
    }

    @Override
    public void accept(TreeVisitor visitor) {
      visitor.visitParameterizedType(this);
    }

    @Override
    public List children() {
      return ListUtils.concat(annotations, Arrays.asList(type, typeArguments));
    }
  }

  public static class ArrayTypeTreeImpl extends AssessableExpressionTree implements ArrayTypeTree, AnnotatedTypeTree {
    private TypeTree type;
    private List annotations;
    private final InternalSyntaxToken openBracketToken;
    private final InternalSyntaxToken closeBracketToken;
    private final InternalSyntaxToken ellipsisToken;

    public ArrayTypeTreeImpl(@Nullable TypeTree type, List annotations, InternalSyntaxToken openBracketToken, InternalSyntaxToken closeBracketToken) {
      this.type = type;
      this.annotations = Collections.unmodifiableList(annotations);
      this.openBracketToken = openBracketToken;
      this.closeBracketToken = closeBracketToken;
      this.ellipsisToken = null;
    }

    public ArrayTypeTreeImpl(@Nullable TypeTree type, List annotations, InternalSyntaxToken ellispsisToken) {
      this.type = type;
      this.annotations = Collections.unmodifiableList(annotations);
      this.openBracketToken = null;
      this.closeBracketToken = null;
      this.ellipsisToken = ellispsisToken;
    }

    public void completeType(TypeTree type) {
      this.type = type;
    }

    public void setLastChildType(TypeTree type) {
      ArrayTypeTree childType = this;
      while (childType.type() != null && childType.is(Tree.Kind.ARRAY_TYPE)) {
        childType = (ArrayTypeTree) childType.type();
      }
      ((ArrayTypeTreeImpl) childType).completeType(type);
    }

    @Override
    public Kind kind() {
      return Kind.ARRAY_TYPE;
    }

    @Override
    public TypeTree type() {
      return type;
    }

    @Override
    public void accept(TreeVisitor visitor) {
      visitor.visitArrayType(this);
    }

    @Override
    public List children() {
      boolean hasBrackets = ellipsisToken == null;
      return ListUtils.concat(
        Collections.singletonList(type),
        annotations,
        hasBrackets ? Arrays.asList(openBracketToken, closeBracketToken) : Collections.singletonList(ellipsisToken));
    }

    @Override
    public List annotations() {
      return annotations;
    }

    @Override
    public SyntaxToken openBracketToken() {
      return openBracketToken;
    }

    @Override
    public SyntaxToken closeBracketToken() {
      return closeBracketToken;
    }

    @Override
    public SyntaxToken ellipsisToken() {
      return ellipsisToken;
    }

    @Override
    public void complete(List typeAnnotations) {
      this.annotations = typeAnnotations;
    }
  }

  /**
   * This interface is dedicated to mark TypeTrees which will requires completion of their annotations during parsing.
   *
   * Note that {@link org.sonar.plugins.java.api.tree.InferedTypeTree} and {@link UnionTypeTree} can not have annotations.
   */
  public interface AnnotatedTypeTree extends TypeTree {
    void complete(List annotations);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy