blob: b1d8978eea68bfb43397ff1f98174a359419e44a [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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.intellij.openapi.vcs.changes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.impl.VcsPathPresenter;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcsUtil.VcsFilePathUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
/**
* @author max
*/
public class Change {
private int myHash;
public enum Type {
MODIFICATION,
NEW,
DELETED,
MOVED
}
private final ContentRevision myBeforeRevision;
private final ContentRevision myAfterRevision;
private final FileStatus myFileStatus;
protected String myMoveRelativePath;
protected boolean myRenamed;
protected boolean myMoved;
protected boolean myRenameOrMoveCached = false;
private boolean myIsReplaced;
private Type myType;
private final Map<String, Change> myOtherLayers;
// if null, vcs's is used. intended: for property conflict case
private Getter<MergeTexts> myMergeProvider;
public Change(@Nullable final ContentRevision beforeRevision, @Nullable final ContentRevision afterRevision) {
this(beforeRevision, afterRevision, convertStatus(beforeRevision, afterRevision));
}
public Change(@Nullable final ContentRevision beforeRevision, @Nullable final ContentRevision afterRevision, @Nullable FileStatus fileStatus) {
assert beforeRevision != null || afterRevision != null;
myBeforeRevision = beforeRevision;
myAfterRevision = afterRevision;
myFileStatus = fileStatus == null ? convertStatus(beforeRevision, afterRevision) : fileStatus;
myHash = -1;
myOtherLayers = new HashMap<String, Change>(0);
}
private static FileStatus convertStatus(@Nullable ContentRevision beforeRevision, @Nullable ContentRevision afterRevision) {
if (beforeRevision == null) return FileStatus.ADDED;
if (afterRevision == null) return FileStatus.DELETED;
return FileStatus.MODIFIED;
}
public Getter<MergeTexts> getMergeProvider() {
return myMergeProvider;
}
public void setMergeProvider(Getter<MergeTexts> mergeProvider) {
myMergeProvider = mergeProvider;
}
public void addAdditionalLayerElement(final String name, final Change change) {
myOtherLayers.put(name, change);
}
public Map<String, Change> getOtherLayers() {
return myOtherLayers;
}
public boolean isTreeConflict() {
return false;
}
public boolean isPhantom() {
return false;
}
public boolean hasOtherLayers() {
return ! myOtherLayers.isEmpty();
}
public Type getType() {
if (myType == null) {
if (myBeforeRevision == null) {
myType = Type.NEW;
return myType;
}
if (myAfterRevision == null) {
myType = Type.DELETED;
return myType;
}
if ((! Comparing.equal(myBeforeRevision.getFile(), myAfterRevision.getFile())) ||
((! SystemInfo.isFileSystemCaseSensitive) && VcsFilePathUtil
.caseDiffers(myBeforeRevision.getFile().getPath(), myAfterRevision.getFile().getPath()))) {
myType = Type.MOVED;
return myType;
}
myType = Type.MODIFICATION;
}
return myType;
}
@Nullable
public ContentRevision getBeforeRevision() {
return myBeforeRevision;
}
@Nullable
public ContentRevision getAfterRevision() {
return myAfterRevision;
}
public FileStatus getFileStatus() {
return myFileStatus;
}
@Nullable
public VirtualFile getVirtualFile() {
return myAfterRevision == null ? null : myAfterRevision.getFile().getVirtualFile();
}
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || (! (o instanceof Change))) return false;
final Change otherChange = ((Change)o);
final ContentRevision br1 = getBeforeRevision();
final ContentRevision br2 = otherChange.getBeforeRevision();
final ContentRevision ar1 = getAfterRevision();
final ContentRevision ar2 = otherChange.getAfterRevision();
FilePath fbr1 = br1 != null ? br1.getFile() : null;
FilePath fbr2 = br2 != null ? br2.getFile() : null;
FilePath far1 = ar1 != null ? ar1.getFile() : null;
FilePath far2 = ar2 != null ? ar2.getFile() : null;
return Comparing.equal(fbr1, fbr2) && Comparing.equal(far1, far2);
}
public int hashCode() {
if (myHash == -1) {
myHash = calculateHash();
}
return myHash;
}
private int calculateHash() {
return revisionHashCode(getBeforeRevision()) * 27 + revisionHashCode(getAfterRevision());
}
private static int revisionHashCode(ContentRevision rev) {
if (rev == null) return 0;
return rev.getFile().getIOFile().getPath().hashCode();
}
public boolean affectsFile(File ioFile) {
if (myBeforeRevision != null && myBeforeRevision.getFile().getIOFile().equals(ioFile)) return true;
if (myAfterRevision != null && myAfterRevision.getFile().getIOFile().equals(ioFile)) return true;
return false;
}
public boolean isRenamed() {
cacheRenameOrMove(null);
return myRenamed;
}
public boolean isMoved() {
cacheRenameOrMove(null);
return myMoved;
}
public String getMoveRelativePath(Project project) {
cacheRenameOrMove(project);
return myMoveRelativePath;
}
private void cacheRenameOrMove(final Project project) {
if (myBeforeRevision != null && myAfterRevision != null && (! revisionPathsSame())) {
if (!myRenameOrMoveCached) {
myRenameOrMoveCached = true;
if (Comparing.equal(myBeforeRevision.getFile().getParentPath(), myAfterRevision.getFile().getParentPath())) {
myRenamed = true;
}
else {
myMoved = true;
}
}
if (myMoved && myMoveRelativePath == null && project != null) {
myMoveRelativePath = VcsPathPresenter.getInstance(project).getPresentableRelativePath(myBeforeRevision, myAfterRevision);
}
}
}
private boolean revisionPathsSame() {
final String path1 = myBeforeRevision.getFile().getIOFile().getAbsolutePath();
final String path2 = myAfterRevision.getFile().getIOFile().getAbsolutePath();
return path1.equals(path2);
}
@NonNls
public String toString() {
final Type type = getType();
//noinspection EnumSwitchStatementWhichMissesCases
switch (type) {
case NEW: return "A: " + myAfterRevision;
case DELETED: return "D: " + myBeforeRevision;
case MOVED: return "M: " + myBeforeRevision + " -> " + myAfterRevision;
default: return "M: " + myAfterRevision;
}
}
@Nullable
public String getOriginText(final Project project) {
cacheRenameOrMove(project);
if (isMoved()) {
return getMovedText(project);
} else if (isRenamed()) {
return getRenamedText();
}
return myIsReplaced ? VcsBundle.message("change.file.replaced.text") : null;
}
@Nullable
protected String getRenamedText() {
return VcsBundle.message("change.file.renamed.from.text", myBeforeRevision.getFile().getName());
}
@Nullable
protected String getMovedText(final Project project) {
return VcsBundle.message("change.file.moved.from.text", getMoveRelativePath(project));
}
public boolean isIsReplaced() {
return myIsReplaced;
}
public void setIsReplaced(final boolean isReplaced) {
myIsReplaced = isReplaced;
}
@Nullable
public Icon getAdditionalIcon() {
return null;
}
@Nullable
public String getDescription() {
return null;
}
}