blob: 415865210477e6acd3c33e87c11719327f2e5fac [file] [log] [blame]
/*
* Copyright 2000-2009 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.
*/
/*
* Created by IntelliJ IDEA.
* User: yole
* Date: 23.11.2006
* Time: 19:06:26
*/
package com.intellij.openapi.vcs.changes.shelf;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diff.impl.patch.ApplyPatchException;
import com.intellij.openapi.diff.impl.patch.PatchSyntaxException;
import com.intellij.openapi.diff.impl.patch.TextFilePatch;
import com.intellij.openapi.diff.impl.patch.apply.GenericPatchApplier;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
public class ShelvedChange {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.shelf.ShelvedChange");
private final String myPatchPath;
private final String myBeforePath;
private final String myAfterPath;
private final FileStatus myFileStatus;
private final AtomicReference<Boolean> myIsConflicting;
private Change myChange;
public ShelvedChange(final String patchPath, final String beforePath, final String afterPath, final FileStatus fileStatus) {
myPatchPath = patchPath;
myBeforePath = beforePath;
// optimisation: memory
myAfterPath = Comparing.equal(beforePath, afterPath) ? beforePath : afterPath;
myFileStatus = fileStatus;
myIsConflicting = new AtomicReference<Boolean>();
}
public boolean isConflictingChange(final Project project) {
Boolean isConflicting = myIsConflicting.get();
if (isConflicting != null) return isConflicting;
ContentRevision afterRevision = getChange(project).getAfterRevision();
if (afterRevision == null) return false;
try {
afterRevision.getContent();
}
catch(VcsException e) {
if (e.getCause() instanceof ApplyPatchException) {
myIsConflicting.set(true);
return true;
}
}
myIsConflicting.set(false);
return false;
}
public String getBeforePath() {
return myBeforePath;
}
@Nullable
public VirtualFile getBeforeVFUnderProject(final Project project) {
if (myBeforePath == null || project.getBaseDir() == null) return null;
final File baseDir = new File(project.getBaseDir().getPath());
final File file = new File(baseDir, myBeforePath);
return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
}
public String getAfterPath() {
return myAfterPath;
}
@Nullable
public String getAfterFileName() {
if (myAfterPath == null) return null;
int pos = myAfterPath.lastIndexOf('/');
if (pos >= 0) return myAfterPath.substring(pos+1);
return myAfterPath;
}
public String getBeforeFileName() {
int pos = myBeforePath.lastIndexOf('/');
if (pos >= 0) return myBeforePath.substring(pos+1);
return myBeforePath;
}
public String getBeforeDirectory() {
int pos = myBeforePath.lastIndexOf('/');
if (pos >= 0) return myBeforePath.substring(0, pos).replace('/', File.separatorChar);
return File.separator;
}
public FileStatus getFileStatus() {
return myFileStatus;
}
public Change getChange(Project project) {
if (myChange == null) {
File baseDir = new File(project.getBaseDir().getPath());
File file = getAbsolutePath(baseDir, myBeforePath);
final FilePathImpl beforePath = new FilePathImpl(file, false);
beforePath.refresh();
ContentRevision beforeRevision = null;
if (myFileStatus != FileStatus.ADDED) {
beforeRevision = new CurrentContentRevision(beforePath) {
@Override
@NotNull
public VcsRevisionNumber getRevisionNumber() {
return new TextRevisionNumber(VcsBundle.message("local.version.title"));
}
};
}
ContentRevision afterRevision = null;
if (myFileStatus != FileStatus.DELETED) {
final FilePathImpl afterPath = new FilePathImpl(getAbsolutePath(baseDir, myAfterPath), false);
afterRevision = new PatchedContentRevision(project, beforePath, afterPath);
}
myChange = new Change(beforeRevision, afterRevision, myFileStatus);
}
return myChange;
}
private static File getAbsolutePath(final File baseDir, final String relativePath) {
File file;
try {
file = new File(baseDir, relativePath).getCanonicalFile();
}
catch (IOException e) {
LOG.info(e);
file = new File(baseDir, relativePath);
}
return file;
}
@Nullable
public TextFilePatch loadFilePatch(final Project project, CommitContext commitContext) throws IOException, PatchSyntaxException {
List<TextFilePatch> filePatches = ShelveChangesManager.loadPatches(project, myPatchPath, commitContext);
for(TextFilePatch patch: filePatches) {
if (myBeforePath.equals(patch.getBeforeName())) {
return patch;
}
}
return null;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof ShelvedChange)) return false;
final ShelvedChange that = (ShelvedChange)o;
if (myAfterPath != null ? !myAfterPath.equals(that.myAfterPath) : that.myAfterPath != null) return false;
if (myBeforePath != null ? !myBeforePath.equals(that.myBeforePath) : that.myBeforePath != null) return false;
if (myFileStatus != null ? !myFileStatus.equals(that.myFileStatus) : that.myFileStatus != null) return false;
if (myPatchPath != null ? !myPatchPath.equals(that.myPatchPath) : that.myPatchPath != null) return false;
return true;
}
@Override
public int hashCode() {
int result = myPatchPath != null ? myPatchPath.hashCode() : 0;
result = 31 * result + (myBeforePath != null ? myBeforePath.hashCode() : 0);
result = 31 * result + (myAfterPath != null ? myAfterPath.hashCode() : 0);
result = 31 * result + (myFileStatus != null ? myFileStatus.hashCode() : 0);
return result;
}
private class PatchedContentRevision implements ContentRevision {
private final Project myProject;
private final FilePath myBeforeFilePath;
private final FilePath myAfterFilePath;
private String myContent;
public PatchedContentRevision(Project project, final FilePath beforeFilePath, final FilePath afterFilePath) {
myProject = project;
myBeforeFilePath = beforeFilePath;
myAfterFilePath = afterFilePath;
}
@Override
@Nullable
public String getContent() throws VcsException {
if (myContent == null) {
try {
myContent = loadContent();
}
catch (Exception e) {
throw new VcsException(e);
}
}
return myContent;
}
@Nullable
private String loadContent() throws IOException, PatchSyntaxException, ApplyPatchException {
TextFilePatch patch = loadFilePatch(myProject, null);
if (patch != null) {
return loadContent(patch);
}
return null;
}
private String loadContent(final TextFilePatch patch) throws ApplyPatchException {
if (patch.isNewFile()) {
return patch.getNewFileText();
}
if (patch.isDeletedFile()) {
return null;
}
final GenericPatchApplier applier = new GenericPatchApplier(getBaseContent(), patch.getHunks());
if (applier.execute()) {
return applier.getAfter();
}
throw new ApplyPatchException("Apply patch conflict");
}
private String getBaseContent() {
myBeforeFilePath.refresh();
final Document doc = FileDocumentManager.getInstance().getDocument(myBeforeFilePath.getVirtualFile());
return doc.getText();
}
@Override
@NotNull
public FilePath getFile() {
return myAfterFilePath;
}
@Override
@NotNull
public VcsRevisionNumber getRevisionNumber() {
return new TextRevisionNumber(VcsBundle.message("shelved.version.name"));
}
}
public String getPatchPath() {
return myPatchPath;
}
}