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

org.sonar.python.checks.cdk.CdkPredicate Maven / Gradle / Ivy

There is a newer version: 4.23.0.17664
Show newest version
/*
 * SonarQube Python Plugin
 * Copyright (C) 2011-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.python.checks.cdk;

import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import org.sonar.plugins.python.api.tree.CallExpression;
import org.sonar.plugins.python.api.tree.Expression;
import org.sonar.plugins.python.api.tree.NumericLiteral;
import org.sonar.plugins.python.api.tree.Token;
import org.sonar.plugins.python.api.tree.Tree;
import org.sonar.python.tree.TreeUtils;

import static org.sonar.python.checks.cdk.CdkUtils.getArgument;

public class CdkPredicate {

  private CdkPredicate() {

  }

  /**
   * @return Predicate which tests if expression is boolean literal and is set to `false`
   */
  public static Predicate isFalse() {
    return expression -> Optional.ofNullable(expression.firstToken()).map(Token::value).filter("False"::equals).isPresent();
  }

  /**
   * @return Predicate which tests if expression is boolean literal and is set to `true`
   */
  public static Predicate isTrue() {
    return expression -> Optional.ofNullable(expression.firstToken()).map(Token::value).filter("True"::equals).isPresent();
  }

  /**
   * @return Predicate which tests if expression is `none`
   */
  public static Predicate isNone() {
    return expression -> expression.is(Tree.Kind.NONE);
  }

  /**
   * @return Predicate which tests if expression is a fully qualified name (FQN) and is equal the expected FQN
   */
  public static Predicate isFqn(String fqnValue) {
    return expression ->  TreeUtils.fullyQualifiedNameFromExpression(expression)
      .filter(fqnValue::equals)
      .isPresent();
  }

  /**
   * @return Predicate which tests if expression is a fully qualified name (FQN) and part of the FQN list
   */
  public static Predicate isFqnOf(Collection fqnValues) {
    return expression ->  TreeUtils.fullyQualifiedNameFromExpression(expression)
      .filter(fqnValues::contains)
      .isPresent();
  }

  /**
   * @return Predicate which tests if expression is a string and is equal the expected value
   */
  public static Predicate isString(String expectedValue) {
    return expression -> CdkUtils.getString(expression).filter(expectedValue::equals).isPresent();
  }

  public static Predicate isWildcard() {
    return isString("*");
  }

  /**
   * @return Predicate which tests if expression is a string and is equal to any of the expected values
   */
  public static Predicate isString(Set expectedValues) {
    return expression -> CdkUtils.getString(expression).filter(expectedValues::contains).isPresent();
  }

  /**
   * @return Predicate which tests if expression is a string matches the pattern
   */
  public static Predicate matches(Pattern pattern) {
    return expression -> CdkUtils.getString(expression).filter(string -> pattern.matcher(string).find()).isPresent();
  }

  /**
   * @return Predicate which tests if expression is a string literal
   */
  public static Predicate isStringLiteral() {
    return expression -> expression.is(Tree.Kind.STRING_LITERAL);
  }

  /**
   * @return Predicate which tests if expression is a number literal
   */
  public static Predicate isNumericLiteral() {
    return expression -> expression.is(Tree.Kind.NUMERIC_LITERAL);
  }

  /**
   * @return Predicate which tests if expression is a list literal
   */
  public static Predicate isListLiteral() {
    return expression -> expression.is(Tree.Kind.LIST_LITERAL);
  }

  public static Predicate isSubscriptionExpression() {
    return expression -> expression.is(Tree.Kind.SUBSCRIPTION);
  }

  /**
   * @return Predicate which tests if expression is a string and starts with the expected value
   */
  public static Predicate startsWith(String expected) {
    return expression -> CdkUtils.getString(expression).filter(str -> str.toLowerCase(Locale.ROOT).startsWith(expected)).isPresent();
  }

  public static Predicate isCallExpression() {
    return expression -> expression.is(Tree.Kind.CALL_EXPR);
  }

  // Predicate on a Expression that is expected to be a CallExpression with a specific argument (name/pos) on which predicates are applicable
  @SafeVarargs
  public static Predicate hasArgument(String name, int pos, Predicate... predicates) {
    return expression -> {
      if (!expression.is(Tree.Kind.CALL_EXPR)) {
        return false;
      }

      return getArgument(null, (CallExpression) expression, name, pos)
        .filter(flow -> flow.hasExpression(Arrays.stream(predicates).reduce(x -> true, Predicate::and))).isPresent();
    };
  }

  @SafeVarargs
  public static Predicate hasArgument(String name, Predicate... predicates) {
    return hasArgument(name, -1, predicates);
  }

  public static Predicate hasIntervalArguments(String argNameMin, int argPosMin, String argNameMax, int argPosMax, Collection values) {
    return expression -> {
      if (!expression.is(Tree.Kind.CALL_EXPR)) {
        return false;
      }

      Optional minVal = getArgumentAsLong((CallExpression) expression, argNameMin, argPosMin);
      Optional maxVal = getArgumentAsLong((CallExpression) expression, argNameMax, argPosMax);

      if (minVal.isEmpty() || maxVal.isEmpty()) {
        return false;
      }

      return anyValueInInterval(values, minVal.get(), maxVal.get());
    };
  }

  public static Predicate hasIntervalArguments(String argNameMin, String argNameMax, Collection values) {
    return hasIntervalArguments(argNameMin, -1, argNameMax, -1, values);
  }

  private static Optional getArgumentAsLong(CallExpression callExpression, String argName, int argPos) {
    return getArgument(null, callExpression, argName, argPos)
      .flatMap(flow -> flow.getExpression(isNumericLiteral()))
      .map(NumericLiteral.class::cast)
      .map(NumericLiteral::valueAsLong);
  }

  private static boolean anyValueInInterval(Collection values, long min, long max) {
    for (long val : values) {
      if (min <= val && val <= max) {
        return true;
      }
    }
    return false;
  }

  public static Predicate isNumeric(Set vals) {
    return expression -> expression.is(Tree.Kind.NUMERIC_LITERAL) && vals.contains(((NumericLiteral) expression).valueAsLong());
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy