| // Copyright (C) 2022 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.gerrit.plugins.codeowners.backend; |
| |
| import static com.google.common.base.Preconditions.checkState; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.gerrit.entities.PatchSet; |
| import com.google.gerrit.exceptions.StorageException; |
| import com.google.gerrit.plugins.codeowners.backend.config.CodeOwnersPluginProjectConfigSnapshot; |
| import com.google.gerrit.plugins.codeowners.common.ChangedFile; |
| import com.google.gerrit.server.notedb.ChangeNotes; |
| import com.google.gerrit.server.patch.DiffNotAvailableException; |
| import com.google.inject.Inject; |
| import com.google.inject.assistedinject.Assisted; |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * Computes and caches the {@link ChangedFile}s for the patch sets of a change. |
| * |
| * <p>The changed files for a patch set are computed lazily. This way we do not compute changed |
| * files unnecessarily that are never requested. |
| * |
| * <p>This class is not thread-safe. |
| */ |
| public class ChangedFilesByPatchSetCache { |
| interface Factory { |
| ChangedFilesByPatchSetCache create( |
| CodeOwnersPluginProjectConfigSnapshot codeOwnersConfig, ChangeNotes changeNotes); |
| } |
| |
| private final ChangedFiles changedFiles; |
| private final CodeOwnersPluginProjectConfigSnapshot codeOwnersConfig; |
| private final ChangeNotes changeNotes; |
| |
| private Map<PatchSet.Id, ImmutableList<ChangedFile>> cache = new HashMap<>(); |
| |
| @Inject |
| public ChangedFilesByPatchSetCache( |
| ChangedFiles changedFiles, |
| @Assisted CodeOwnersPluginProjectConfigSnapshot codeOwnersConfig, |
| @Assisted ChangeNotes changeNotes) { |
| this.changedFiles = changedFiles; |
| this.codeOwnersConfig = codeOwnersConfig; |
| this.changeNotes = changeNotes; |
| } |
| |
| public ImmutableList<ChangedFile> get(PatchSet.Id patchSetId) { |
| return cache.computeIfAbsent(patchSetId, this::compute); |
| } |
| |
| private ImmutableList<ChangedFile> compute(PatchSet.Id patchSetId) { |
| checkState( |
| patchSetId.changeId().equals(changeNotes.getChange().getId()), |
| "patch set %s belongs to other change than change %s", |
| patchSetId, |
| changeNotes.getChange().getId().get()); |
| PatchSet patchSet = getPatchSet(patchSetId); |
| try { |
| return changedFiles.getFromDiffCache( |
| changeNotes.getProjectName(), |
| patchSet.commitId(), |
| codeOwnersConfig.getMergeCommitStrategy()); |
| } catch (IOException | DiffNotAvailableException e) { |
| throw new StorageException( |
| String.format( |
| "failed to retrieve changed files for patch set %d of change %d" |
| + " in project %s (commit=%s)", |
| patchSetId.get(), |
| patchSetId.changeId().get(), |
| changeNotes.getProjectName(), |
| patchSet.commitId()), |
| e); |
| } |
| } |
| |
| private PatchSet getPatchSet(PatchSet.Id patchSetId) { |
| PatchSet patchSet = changeNotes.getPatchSets().get(patchSetId); |
| if (patchSet == null) { |
| throw new StorageException( |
| String.format( |
| "patch set %s not found in change %d", patchSetId, changeNotes.getChangeId().get())); |
| } |
| return patchSet; |
| } |
| } |