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

org.sonar.java.model.JUtils Maven / Gradle / Ivy

/*
 * SonarQube Java
 * Copyright (C) 2012-2024 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 Sonar Source-Available License Version 1, as published by SonarSource SA.
 *
 * 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 Sonar Source-Available License for more details.
 *
 * You should have received a copy of the Sonar Source-Available License
 * along with this program; if not, see https://sonarsource.com/license/ssal/
 */
package org.sonar.java.model;

import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.annotation.Nullable;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.semantic.SymbolMetadata;
import org.sonar.plugins.java.api.semantic.Type;
import org.sonar.plugins.java.api.tree.ClassTree;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonarsource.analyzer.commons.collections.MapBuilder;

public final class JUtils {

  private JUtils() {
  }

  static final Map WRAPPER_TO_PRIMITIVE = MapBuilder.newMap()
    .put("java.lang.Byte", "byte")
    .put("java.lang.Character", "char")
    .put("java.lang.Short", "short")
    .put("java.lang.Integer", "int")
    .put("java.lang.Long", "long")
    .put("java.lang.Float", "float")
    .put("java.lang.Double", "double")
    .put("java.lang.Boolean", "boolean")
    .build();

  static final Map PRIMITIVE_TO_WRAPPER = MapBuilder.newMap()
    .put("byte", "java.lang.Byte")
    .put("char", "java.lang.Character")
    .put("short", "java.lang.Short")
    .put("int", "java.lang.Integer")
    .put("long","java.lang.Long")
    .put("float", "java.lang.Float")
    .put("double", "java.lang.Double")
    .put("boolean", "java.lang.Boolean")
    .build();

  public static Type wrapTypeIfPrimitive(Type type) {
    Type wrapped = type.primitiveWrapperType();
    return Objects.requireNonNullElse(wrapped, type);
  }

  public static boolean isIntersectionType(Type type) {
    return !type.isUnknown() && ((JType) type).typeBinding.isIntersectionType();
  }

  public static void collectSuperTypes(Set result, JSema sema, ITypeBinding typeBinding) {
    ITypeBinding s = typeBinding.getSuperclass();
    if (s != null) {
      result.add(sema.type(s));
      collectSuperTypes(result, sema, s);
    }
    for (ITypeBinding i : typeBinding.getInterfaces()) {
      result.add(sema.type(i));
      collectSuperTypes(result, sema, i);
    }
  }

  public static Symbol getPackage(Symbol symbol) {
    while (!symbol.isPackageSymbol()) {
      symbol = symbol.owner();
    }
    return symbol;
  }

  @Nullable
  public static Object defaultValue(Symbol.MethodSymbol method) {
    if (method.isUnknown()) {
      return null;
    }
    return ((JMethodSymbol) method).methodBinding().getDefaultValue();
  }

  public static Set directSuperTypes(Type type) {
    if (type.isUnknown()) {
      return Collections.emptySet();
    }
    Set result = new HashSet<>();
    JType t = (JType) type;
    ITypeBinding superclass = t.typeBinding.getSuperclass();
    if (superclass != null) {
      result.add(t.sema.type(superclass));
    }
    for (ITypeBinding i : t.typeBinding.getInterfaces()) {
      result.add(t.sema.type(i));
    }
    return result;
  }

  @Nullable
  public static Symbol enclosingClass(Tree t) {
    do {
      if (t == null) {
        return null;
      }
      if (t.is(Tree.Kind.CLASS, Tree.Kind.ENUM, Tree.Kind.INTERFACE, Tree.Kind.ANNOTATION_TYPE)) {
        return ((ClassTree) t).symbol();
      }
      t = t.parent();
    } while (true);
  }

  public static SymbolMetadata parameterAnnotations(Symbol.MethodSymbol method, int param) {
    if (method.isUnknown()) {
      return Symbols.EMPTY_METADATA;
    }
    return method.declarationParameters().get(param).metadata();
  }

  public static boolean hasUnknownTypePreventingOverrideResolution(Symbol.MethodSymbol symbol) {
    Symbol owner = symbol.owner();
    if (owner == null || !owner.isTypeSymbol()) {
      // Broken hierarchy
      return true;
    }
    boolean hasUnknownParameterType = symbol.parameterTypes().stream().anyMatch(Type::isUnknown);
    if (hasUnknownParameterType) {
      return hasUnknownTypeInHierarchyOrAnyMatchingMethod((Symbol.TypeSymbol) owner, symbol);
    } else {
      return hasUnknownTypeInHierarchy((Symbol.TypeSymbol) owner);
    }
  }

  public static boolean hasUnknownTypeInHierarchy(Symbol.TypeSymbol typeSymbol) {
    if (typeSymbol.isUnknown()) {
      return true;
    }
    if (typeSymbol.interfaces().stream().map(Type::symbol).anyMatch(JUtils::hasUnknownTypeInHierarchy)) {
      return true;
    }
    Type superClass = typeSymbol.superClass();
    if (superClass == null) {
      return false;
    }
    return hasUnknownTypeInHierarchy(superClass.symbol());
  }

  /**
   * @param typeSymbol to lookup recursively for unknown symbol on itself, its interfaces and its super classes hierarchy.
   * @param methodSymbol a method having at least one unknown parameter type that prevent the overriding resolution
   *                    to find any exact match. This methodSymbol will be used to find a potential match, just by
   *                    comparing the name and parameter count.
   * @return true if the given typeSymbol and its inheritance hierarchy is 100% known and no method in the inheritance
   * cloud match (name and parameter count) the given methodSymbol. In this case, we are sure the methodSymbol does not
   * override any another methods.
   */
  private static boolean hasUnknownTypeInHierarchyOrAnyMatchingMethod(Symbol.TypeSymbol typeSymbol, Symbol.MethodSymbol methodSymbol) {
    if (typeSymbol.isUnknown()) {
      return true;
    }
    if (typeSymbol != methodSymbol.owner() && typeSymbol.memberSymbols().stream()
      .anyMatch(member -> member.isMethodSymbol() &&
        ((Symbol.MethodSymbol) member).parameterTypes().size() == methodSymbol.parameterTypes().size() &&
        methodSymbol.name().equals(member.name()))) {
      return true;
    }
    if (typeSymbol.interfaces().stream().map(Type::symbol)
      .anyMatch(interfaceSymbol -> hasUnknownTypeInHierarchyOrAnyMatchingMethod(interfaceSymbol, methodSymbol))) {
      return true;
    }
    Type superClass = typeSymbol.superClass();
    if (superClass == null) {
      return false;
    }
    return hasUnknownTypeInHierarchyOrAnyMatchingMethod(superClass.symbol(), methodSymbol);
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy