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

au.com.integradev.delphi.checks.verifier.Expectations Maven / Gradle / Ivy

The newest version!
/*
 * Sonar Delphi Plugin
 * Copyright (C) 2024 Integrated Application Development
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package au.com.integradev.delphi.checks.verifier;

import au.com.integradev.delphi.file.DelphiFile.DelphiInputFile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.sonar.plugins.communitydelphi.api.token.DelphiToken;

class Expectations {
  private static final Pattern NONCOMPLIANT_PATTERN =
      Pattern.compile("(?i)^//\\s*Noncompliant(?:@([-+]\\d+))?\\b");

  private static final Pattern QUICK_FIX_RANGE_PATTERN =
      Pattern.compile("^([+-]\\d+):(\\d*) to ([+-]\\d+):(\\d*)$");

  private static final Pattern QUICK_FIX_PATTERN =
      Pattern.compile("(?i)^//\\s*Fix\\s*(\\w+)?@\\s*\\[([^]]*)]\\s*(?:<<(.*?)>>)?");

  private final List expectedIssues;
  private final List expectedQuickFixes;

  private Expectations(List issues, List quickFixes) {
    this.expectedIssues = issues;
    this.expectedQuickFixes = quickFixes;
  }

  public List issues() {
    return Collections.unmodifiableList(expectedIssues);
  }

  public List quickFixes() {
    return Collections.unmodifiableList(expectedQuickFixes);
  }

  public static Expectations fromComments(DelphiInputFile delphiFile) {
    return parse(delphiFile.getComments());
  }

  private static Expectations parse(List comments) {
    List noncompliantComments = new ArrayList<>();
    List fixComments = new ArrayList<>();

    for (DelphiToken comment : comments) {
      var matcher = NONCOMPLIANT_PATTERN.matcher(comment.getImage());
      if (matcher.matches()) {
        noncompliantComments.add(
            new MatchResultOnLine(matcher.toMatchResult(), comment.getBeginLine()));
      } else {
        matcher = QUICK_FIX_PATTERN.matcher(comment.getImage());
        if (matcher.matches()) {
          fixComments.add(new MatchResultOnLine(matcher.toMatchResult(), comment.getBeginLine()));
        }
      }
    }

    List issues =
        noncompliantComments.stream()
            .map(r -> parseNoncompliantComment(r.getLine(), r.getMatchResult()))
            .collect(Collectors.toList());

    List quickFixes =
        fixComments.stream()
            .map(r -> parseFixComment(r.getLine(), r.getMatchResult()))
            .collect(Collectors.groupingBy(TextEditExpectation::getFixId))
            .entrySet()
            .stream()
            .map(entry -> new QuickFixExpectation(entry.getKey(), entry.getValue()))
            .collect(Collectors.toList());

    return new Expectations(issues, quickFixes);
  }

  private static TextEditExpectation parseFixComment(int offset, MatchResult matchResult) {
    String fixId = matchResult.group(1);
    String rangeStr = matchResult.group(2);
    String replacementStr = matchResult.group(3);

    if (replacementStr == null) {
      throw new AssertionError(
          String.format(
              "Replacement text must be specified for quick fix edit on line %d", offset));
    }

    var rangeMatcher = QUICK_FIX_RANGE_PATTERN.matcher(rangeStr);

    if (!rangeMatcher.matches()) {
      throw new AssertionError(
          String.format("Invalid range '%s' for quick fix edit on line %d", rangeStr, offset));
    }

    String beginLineStr = rangeMatcher.group(1);
    String beginColumnStr = rangeMatcher.group(2);
    String endLineStr = rangeMatcher.group(3);
    String endColumnStr = rangeMatcher.group(4);

    int beginLine = beginLineStr != null ? Integer.parseInt(beginLineStr) : 0;
    int endLine = endLineStr != null ? Integer.parseInt(endLineStr) : 0;

    return new TextEditExpectation(
        fixId == null ? "(unnamed)" : fixId,
        replacementStr.replace("\\n", "\n").replace("\\r", "\r"),
        beginLine + offset,
        endLine + offset,
        Integer.parseInt(beginColumnStr),
        Integer.parseInt(endColumnStr));
  }

  private static IssueExpectation parseNoncompliantComment(int beginLine, MatchResult matchResult) {
    String offset = matchResult.group(1);
    if (offset == null) {
      return new IssueExpectation(beginLine);
    }

    try {
      return new IssueExpectation(beginLine + Integer.parseInt(offset));
    } catch (NumberFormatException e) {
      throw new AssertionError(
          String.format(
              "Failed to parse 'Noncompliant' comment line offset '%s' as an integer.", offset));
    }
  }

  private static class MatchResultOnLine {
    private final MatchResult matchResult;
    private final int line;

    public MatchResultOnLine(MatchResult result, int line) {
      this.matchResult = result;
      this.line = line;
    }

    public MatchResult getMatchResult() {
      return matchResult;
    }

    public int getLine() {
      return line;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy