http://ea.jetbrains.com/browser/ea_problems/17771?report=215989
[fedora-idea.git] / platform / vcs-impl / src / com / intellij / openapi / vcs / changes / patch / ApplyPatchAction.java
blob9026e98f86a99ef396940f71b0fc35521b46944d
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Created by IntelliJ IDEA.
19 * User: yole
20 * Date: 17.11.2006
21 * Time: 17:08:11
23 package com.intellij.openapi.vcs.changes.patch;
25 import com.intellij.openapi.actionSystem.ActionPlaces;
26 import com.intellij.openapi.actionSystem.AnAction;
27 import com.intellij.openapi.actionSystem.AnActionEvent;
28 import com.intellij.openapi.actionSystem.PlatformDataKeys;
29 import com.intellij.openapi.diagnostic.Logger;
30 import com.intellij.openapi.diff.ActionButtonPresentation;
31 import com.intellij.openapi.diff.DiffManager;
32 import com.intellij.openapi.diff.DiffRequestFactory;
33 import com.intellij.openapi.diff.MergeRequest;
34 import com.intellij.openapi.diff.impl.patch.*;
35 import com.intellij.openapi.diff.impl.patch.formove.PatchApplier;
36 import com.intellij.openapi.fileEditor.FileDocumentManager;
37 import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
38 import com.intellij.openapi.fileTypes.StdFileTypes;
39 import com.intellij.openapi.project.Project;
40 import com.intellij.openapi.ui.DialogWrapper;
41 import com.intellij.openapi.ui.Messages;
42 import com.intellij.openapi.vcs.*;
43 import com.intellij.openapi.vfs.VirtualFile;
44 import com.intellij.util.Consumer;
45 import com.intellij.util.containers.Convertor;
46 import com.intellij.util.containers.MultiMap;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
50 import java.util.Collection;
51 import java.util.LinkedList;
52 import java.util.List;
54 public class ApplyPatchAction extends AnAction {
55 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.patch.ApplyPatchAction");
57 public void actionPerformed(AnActionEvent e) {
58 final Project project = e.getData(PlatformDataKeys.PROJECT);
60 final Consumer<ApplyPatchDifferentiatedDialog> callback = new Consumer<ApplyPatchDifferentiatedDialog>() {
61 public void consume(ApplyPatchDifferentiatedDialog newDia) {
62 if (newDia.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
63 return;
66 final Collection<FilePatchInProgress> included = newDia.getIncluded();
67 final MultiMap<VirtualFile, FilePatchInProgress> patchGroups = new MultiMap<VirtualFile, FilePatchInProgress>();
68 for (FilePatchInProgress patchInProgress : included) {
69 patchGroups.putValue(patchInProgress.getBase(), patchInProgress);
72 final Collection<PatchApplier> appliers = new LinkedList<PatchApplier>();
73 for (VirtualFile base : patchGroups.keySet()) {
74 final PatchApplier patchApplier =
75 new PatchApplier(project, base, ObjectsConvertor.convert(patchGroups.get(base), new Convertor<FilePatchInProgress, FilePatch>() {
76 public FilePatch convert(FilePatchInProgress o) {
77 return o.getPatch();
79 }), newDia.getSelectedChangeList(), null);
80 appliers.add(patchApplier);
82 PatchApplier.executePatchGroup(appliers);
85 FileDocumentManager.getInstance().saveAllDocuments();
86 final ApplyPatchDifferentiatedDialog dialog = new ApplyPatchDifferentiatedDialog(project, callback);
87 dialog.show();
90 public static void applySkipDirs(final List<FilePatch> patches, final int skipDirs) {
91 if (skipDirs < 1) {
92 return;
94 for (FilePatch patch : patches) {
95 patch.setBeforeName(skipN(patch.getBeforeName(), skipDirs));
96 patch.setAfterName(skipN(patch.getAfterName(), skipDirs));
100 private static String skipN(final String path, final int num) {
101 final String[] pieces = path.split("/");
102 final StringBuilder sb = new StringBuilder();
103 for (int i = num; i < pieces.length; i++) {
104 final String piece = pieces[i];
105 sb.append('/').append(piece);
107 return sb.toString();
110 public static ApplyPatchStatus applyOnly(final Project project, final FilePatch patch, final ApplyPatchContext context, final VirtualFile file) {
111 try {
112 return patch.apply(file, context, project);
114 catch(ApplyPatchException ex) {
115 if (!patch.isNewFile() && !patch.isDeletedFile() && patch instanceof TextFilePatch) {
116 //final VirtualFile beforeRename = (pathBeforeRename == null) ? file : pathBeforeRename;
117 ApplyPatchStatus mergeStatus = mergeAgainstBaseVersion(project, file, new FilePathImpl(file), (TextFilePatch) patch,
118 ApplyPatchMergeRequestFactory.INSTANCE);
119 if (mergeStatus != null) {
120 return mergeStatus;
123 Messages.showErrorDialog(project, VcsBundle.message("patch.apply.error", patch.getBeforeName(), ex.getMessage()),
124 VcsBundle.message("patch.apply.dialog.title"));
126 catch (Exception ex) {
127 LOG.error(ex);
129 return ApplyPatchStatus.FAILURE;
132 @Nullable
133 public static ApplyPatchStatus mergeAgainstBaseVersion(Project project, VirtualFile file, ApplyPatchContext context,
134 final TextFilePatch patch,
135 final PatchMergeRequestFactory mergeRequestFactory) {
136 final FilePath pathBeforeRename = context.getPathBeforeRename(file);
137 return mergeAgainstBaseVersion(project, file, pathBeforeRename, patch, mergeRequestFactory);
140 @Nullable
141 public static ApplyPatchStatus mergeAgainstBaseVersion(final Project project, final VirtualFile file, final FilePath pathBeforeRename,
142 final TextFilePatch patch, final PatchMergeRequestFactory mergeRequestFactory) {
143 final ApplyPatchForBaseRevisionTexts threeTexts = ApplyPatchForBaseRevisionTexts.create(project, file, pathBeforeRename, patch);
144 if (threeTexts == null) {
145 return null;
147 ApplyPatchStatus status = threeTexts.getStatus();
148 if (ApplyPatchStatus.FAILURE.equals(status)) {
149 final VcsException vcsExc = threeTexts.getException();
150 Messages.showErrorDialog(project, VcsBundle.message("patch.load.base.revision.error", patch.getBeforeName(),
151 vcsExc == null ? null : vcsExc.getMessage()), VcsBundle.message("patch.apply.dialog.title"));
152 return status;
154 if (status != ApplyPatchStatus.ALREADY_APPLIED) {
155 return showMergeDialog(project, file, threeTexts.getBase(), threeTexts.getPatched(), mergeRequestFactory);
157 else {
158 return status;
162 private static ApplyPatchStatus showMergeDialog(Project project, VirtualFile file, CharSequence content, final String patchedContent,
163 final PatchMergeRequestFactory mergeRequestFactory) {
164 CharSequence fileContent = LoadTextUtil.loadText(file);
165 if (fileContent == null || content == null) {
166 return ApplyPatchStatus.FAILURE;
168 final MergeRequest request = mergeRequestFactory.createMergeRequest(fileContent.toString(), patchedContent, content.toString(), file,
169 project);
170 DiffManager.getInstance().getDiffTool().show(request);
171 if (request.getResult() == DialogWrapper.OK_EXIT_CODE) {
172 return ApplyPatchStatus.SUCCESS;
174 request.restoreOriginalContent();
175 return ApplyPatchStatus.FAILURE;
178 @Override
179 public void update(AnActionEvent e) {
180 Project project = e.getData(PlatformDataKeys.PROJECT);
181 if (e.getPlace().equals(ActionPlaces.PROJECT_VIEW_POPUP)) {
182 VirtualFile vFile = e.getData(PlatformDataKeys.VIRTUAL_FILE);
183 e.getPresentation().setVisible(project != null && vFile != null && vFile.getFileType() == StdFileTypes.PATCH);
185 else {
186 e.getPresentation().setEnabled(project != null);
190 public static class ApplyPatchMergeRequestFactory implements PatchMergeRequestFactory {
191 private final boolean myReadOnly;
193 public static final ApplyPatchMergeRequestFactory INSTANCE = new ApplyPatchMergeRequestFactory(false);
194 public static final ApplyPatchMergeRequestFactory INSTANCE_READ_ONLY = new ApplyPatchMergeRequestFactory(true);
196 public ApplyPatchMergeRequestFactory(final boolean readOnly) {
197 myReadOnly = readOnly;
200 public MergeRequest createMergeRequest(final String leftText, final String rightText, final String originalContent, @NotNull final VirtualFile file,
201 final Project project) {
202 MergeRequest request;
203 if (myReadOnly) {
204 request = DiffRequestFactory.getInstance().create3WayDiffRequest(leftText, rightText, originalContent, project, null);
205 } else {
206 request = DiffRequestFactory.getInstance().createMergeRequest(leftText, rightText, originalContent,
207 file, project, ActionButtonPresentation.createApplyButton());
210 request.setVersionTitles(new String[] {
211 VcsBundle.message("patch.apply.conflict.local.version"),
212 VcsBundle.message("patch.apply.conflict.merged.version"),
213 VcsBundle.message("patch.apply.conflict.patched.version")
215 request.setWindowTitle(VcsBundle.message("patch.apply.conflict.title", file.getPresentableUrl()));
216 return request;