blob: 267e3c718a9924def6a8ed4caa2ff7a4faeb51d3 [file] [log] [blame]
// Copyright (C) 2020 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.common;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Patch;
import com.google.gerrit.plugins.codeowners.util.JgitPath;
import com.google.gerrit.server.patch.PatchListEntry;
import com.google.gerrit.server.patch.filediff.FileDiffOutput;
import java.nio.file.Path;
import java.util.Optional;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
/**
* A file that was touched in a change.
*
* <p>Wrapper around {@link DiffEntry} that provides convenience methods to access data from {@link
* DiffEntry} and hides data that is not relevant for the code owners plugin.
*/
@AutoValue
public abstract class ChangedFile {
private static final ImmutableMap<Patch.ChangeType, ChangeType> CHANGE_TYPE =
Maps.immutableEnumMap(
new ImmutableMap.Builder<Patch.ChangeType, ChangeType>()
.put(Patch.ChangeType.ADDED, ChangeType.ADD)
.put(Patch.ChangeType.MODIFIED, ChangeType.MODIFY)
.put(Patch.ChangeType.DELETED, ChangeType.DELETE)
.put(Patch.ChangeType.RENAMED, ChangeType.RENAME)
.put(Patch.ChangeType.COPIED, ChangeType.COPY)
.put(Patch.ChangeType.REWRITE, ChangeType.MODIFY)
.build());
/**
* The new path of the file.
*
* <p>Not set if the file was deleted.
*
* <p>If set, the new path is returned as absolute path.
*/
public abstract Optional<Path> newPath();
/**
* The old path of the file.
*
* <p>Not set if the file was newly added
*
* <p>If set, the old path is returned as absolute path.
*/
public abstract Optional<Path> oldPath();
/** Gets the change type. */
public abstract ChangeType changeType();
/**
* Whether the file has the given path as new path.
*
* @param absolutePath an absolute path for which it should be checked if it matches the new path
* of this file
*/
public boolean hasNewPath(Path absolutePath) {
requireNonNull(absolutePath, "absolutePath");
checkState(absolutePath.isAbsolute(), "path %s must be absolute", absolutePath);
return newPath().isPresent() && newPath().get().equals(absolutePath);
}
/**
* Whether the file has the given path as old path.
*
* @param absolutePath an absolute path for which it should be checked if it matches the old path
* of this file
*/
public boolean hasOldPath(Path absolutePath) {
requireNonNull(absolutePath, "absolutePath");
checkState(absolutePath.isAbsolute(), "path %s must be absolute", absolutePath);
return oldPath().isPresent() && oldPath().get().equals(absolutePath);
}
/** Whether the file was renamed. */
public boolean isRename() {
return changeType() == ChangeType.RENAME;
}
/** Whether the file was deleted. */
public boolean isDeletion() {
return changeType() == ChangeType.DELETE;
}
/**
* Creates a {@link ChangedFile} instance from a {@link DiffEntry}.
*
* @param diffEntry the diff entry
*/
public static ChangedFile create(DiffEntry diffEntry) {
requireNonNull(diffEntry, "diffEntry");
return new AutoValue_ChangedFile(
convertPathFromDiffEntryPath(diffEntry.getNewPath()),
convertPathFromDiffEntryPath(diffEntry.getOldPath()),
diffEntry.getChangeType());
}
/**
* Converts the given string path to an absolute path.
*
* <p>{@link DiffEntry} is using {@code /dev/null} if a path doesn't exist. If the given path is
* {@code /dev/null} {@link Optional#empty} is returned.
*/
private static Optional<Path> convertPathFromDiffEntryPath(String path) {
if (DiffEntry.DEV_NULL.equals(path)) {
return Optional.empty();
}
return Optional.of(JgitPath.of(path).getAsAbsolutePath());
}
/**
* Creates a {@link ChangedFile} instance from a {@link PatchListEntry}.
*
* @param patchListEntry the patch list entry
*/
public static ChangedFile create(PatchListEntry patchListEntry) {
requireNonNull(patchListEntry, "patchListEntry");
if (patchListEntry.getChangeType() == Patch.ChangeType.DELETED) {
// For deletions PatchListEntry sets the old path as new name and the old name is unset (see
// PatchListEntry constructor). This means to get the old path we need to read the new name.
return new AutoValue_ChangedFile(
Optional.empty(),
convertPathFromPatchListEntry(patchListEntry.getNewName()),
CHANGE_TYPE.get(patchListEntry.getChangeType()));
}
return new AutoValue_ChangedFile(
convertPathFromPatchListEntry(patchListEntry.getNewName()),
convertPathFromPatchListEntry(patchListEntry.getOldName()),
CHANGE_TYPE.get(patchListEntry.getChangeType()));
}
/**
* Converts the given string path to an absolute path.
*
* <p>{@link PatchListEntry} is using {@code null} if a path doesn't exist. If the given path is
* {@code null} {@link Optional#empty} is returned.
*/
private static Optional<Path> convertPathFromPatchListEntry(@Nullable String path) {
return Optional.ofNullable(path).map(newName -> JgitPath.of(newName).getAsAbsolutePath());
}
/**
* Creates a {@link ChangedFile} instance from a {@link FileDiffOutput}.
*
* @param fileDiffOutput the file diff output
*/
public static ChangedFile create(FileDiffOutput fileDiffOutput) {
requireNonNull(fileDiffOutput, "fileDiffOutput");
return new AutoValue_ChangedFile(
convertPathFromFileDiffOutput(fileDiffOutput.newPath()),
convertPathFromFileDiffOutput(fileDiffOutput.oldPath()),
CHANGE_TYPE.get(fileDiffOutput.changeType()));
}
/** Converts the given string path to an absolute path. */
private static Optional<Path> convertPathFromFileDiffOutput(Optional<String> path) {
requireNonNull(path, "path");
return path.map(p -> JgitPath.of(p).getAsAbsolutePath());
}
public static ChangedFile create(
Optional<String> newPath, Optional<String> oldPath, ChangeType changeType) {
requireNonNull(changeType, "changeType");
return new AutoValue_ChangedFile(
newPath.map(JgitPath::of).map(JgitPath::getAsAbsolutePath),
oldPath.map(JgitPath::of).map(JgitPath::getAsAbsolutePath),
changeType);
}
public static ChangedFile addition(Path newPath) {
requireNonNull(newPath, "newPath");
return new AutoValue_ChangedFile(Optional.of(newPath), Optional.empty(), ChangeType.ADD);
}
public static ChangedFile modification(Path path) {
requireNonNull(path, "path");
return new AutoValue_ChangedFile(Optional.of(path), Optional.of(path), ChangeType.MODIFY);
}
public static ChangedFile deletion(Path path) {
requireNonNull(path, "path");
return new AutoValue_ChangedFile(Optional.empty(), Optional.of(path), ChangeType.DELETE);
}
public static ChangedFile rename(Path newPath, Path oldPath) {
requireNonNull(newPath, "newPath");
return new AutoValue_ChangedFile(Optional.of(newPath), Optional.of(oldPath), ChangeType.RENAME);
}
}