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

org.sonarsource.sonarlint.ls.IssuesCache Maven / Gradle / Ivy

There is a newer version: 3.12.0.75621
Show newest version
/*
 * SonarLint Language Server
 * Copyright (C) 2009-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.sonarsource.sonarlint.ls;

import com.google.gson.JsonObject;
import java.net.URI;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.eclipse.lsp4j.Diagnostic;
import org.sonarsource.sonarlint.core.client.api.common.analysis.Issue;
import org.sonarsource.sonarlint.core.clientapi.backend.hotspot.HotspotStatus;
import org.sonarsource.sonarlint.ls.connected.DelegatingIssue;
import org.sonarsource.sonarlint.ls.file.VersionedOpenFile;

import static org.sonarsource.sonarlint.ls.util.Utils.hotspotReviewStatusValueOfHotspotStatus;
import static org.sonarsource.sonarlint.ls.util.Utils.isDelegatingIssueWithServerIssueKey;

public class IssuesCache {

  private final Map> issuesPerIdPerFileURI = new ConcurrentHashMap<>();
  private final Map> inProgressAnalysisIssuesPerIdPerFileURI = new ConcurrentHashMap<>();

  public void clear(URI fileUri) {
    issuesPerIdPerFileURI.remove(fileUri);
    inProgressAnalysisIssuesPerIdPerFileURI.remove(fileUri);
  }

  /**
   * Keep only the entries for the given set of files
   *
   * @param openFiles the set of file URIs to keep
   * @return the set of file URIs that were removed
   */
  public Set keepOnly(Collection openFiles) {
    var keysBeforeRemoval = new HashSet<>(issuesPerIdPerFileURI.keySet());
    var keysToRetain = openFiles.stream().map(VersionedOpenFile::getUri).collect(Collectors.toSet());
    issuesPerIdPerFileURI.keySet().retainAll(keysToRetain);
    inProgressAnalysisIssuesPerIdPerFileURI.keySet().retainAll(keysToRetain);
    keysBeforeRemoval.removeAll(issuesPerIdPerFileURI.keySet());
    return keysBeforeRemoval;
  }

  public void analysisStarted(VersionedOpenFile versionedOpenFile) {
    inProgressAnalysisIssuesPerIdPerFileURI.remove(versionedOpenFile.getUri());
  }

  public void reportIssue(VersionedOpenFile versionedOpenFile, Issue issue) {
    var issueId = getIssueId(issue);
    inProgressAnalysisIssuesPerIdPerFileURI.computeIfAbsent(versionedOpenFile.getUri(), u -> new HashMap<>()).put(issueId,
      new VersionedIssue(issue, versionedOpenFile.getVersion()));
  }

  private static String getIssueId(Issue issue) {
    String issueId = null;
    if (issue instanceof DelegatingIssue delegatingIssue) {
      var issueUuid = delegatingIssue.getIssueId();
      issueId = issueUuid != null ? issueUuid.toString() : null;
    }
    if (issueId == null) {
      issueId = UUID.randomUUID().toString();
    }
    return issueId;
  }

  public int count(URI f) {
    return get(f).size();
  }

  public void analysisFailed(VersionedOpenFile versionedOpenFile) {
    // Keep issues of the previous analysis
    inProgressAnalysisIssuesPerIdPerFileURI.remove(versionedOpenFile.getUri());
  }

  public void analysisSucceeded(VersionedOpenFile versionedOpenFile) {
    // Swap issues
    var newIssues = inProgressAnalysisIssuesPerIdPerFileURI.remove(versionedOpenFile.getUri());
    if (newIssues != null) {
      issuesPerIdPerFileURI.put(versionedOpenFile.getUri(), newIssues);
    } else {
      issuesPerIdPerFileURI.remove(versionedOpenFile.getUri());
    }
  }

  public void removeIssueWithServerKey(String fileUriStr, String key) {
    var fileUri = URI.create(fileUriStr);
    var issues = issuesPerIdPerFileURI.get(fileUri);
    if (issues != null) {
      var first = issues.entrySet()
        .stream()
        .filter(issueEntry -> isDelegatingIssueWithServerIssueKey(key, issueEntry) || isLocalIssueWithKey(key, issueEntry.getValue()))
        .map(Map.Entry::getKey)
        .findFirst();
      first.ifPresent(issues::remove);
    }
  }

  private static boolean isLocalIssueWithKey(String key, VersionedIssue versionedIssue) {
    return versionedIssue.issue() instanceof DelegatingIssue delegatingIssue
      && (key.equals(delegatingIssue.getIssueId().toString()));
  }

  public Optional> findIssuePerId(String fileUriStr, String serverIssueKey) {
    var fileUri = URI.create(fileUriStr);
    var issues = issuesPerIdPerFileURI.get(fileUri);
    if (issues != null) {
      return issues.entrySet()
        .stream()
        .filter(issueEntry -> isDelegatingIssueWithServerIssueKey(serverIssueKey, issueEntry))
        .findFirst();
    }
    return Optional.empty();
  }

  public void updateIssueStatus(String fileUriStr, String serverIssueKey, HotspotStatus newStatus) {
    var issuePerId = findIssuePerId(fileUriStr, serverIssueKey);
    if (issuePerId.isPresent()) {
      var versionedIssue = issuePerId.get().getValue();
      var delegatingIssue = (DelegatingIssue) versionedIssue.issue();
      var clonedDelegatingIssue = delegatingIssue.cloneWithNewStatus(hotspotReviewStatusValueOfHotspotStatus(newStatus));
      var clonedVersionedIssue = new VersionedIssue(clonedDelegatingIssue, versionedIssue.documentVersion);
      var issuesByKey = issuesPerIdPerFileURI.get(URI.create(fileUriStr));
      if (issuesByKey != null) {
        issuesByKey.put(issuePerId.get().getKey(), clonedVersionedIssue);
      }
    }
  }

  public Optional getIssueForDiagnostic(URI fileUri, Diagnostic d) {
    var issuesForFile = get(fileUri);
    return Optional.ofNullable(d.getData())
      .map(JsonObject.class::cast)
      .map(jsonObject -> jsonObject.get("entryKey").getAsString())
      .map(issuesForFile::get)
      .filter(Objects::nonNull);
  }

  public record VersionedIssue(Issue issue, int documentVersion) {}

  public Map get(URI fileUri) {
    return inProgressAnalysisIssuesPerIdPerFileURI.getOrDefault(fileUri, issuesPerIdPerFileURI.getOrDefault(fileUri, Map.of()));
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy