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.
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
) {
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
) {
79 }), newDia
.getSelectedChangeList(), null);
80 appliers
.add(patchApplier
);
82 PatchApplier
.executePatchGroup(appliers
);
85 FileDocumentManager
.getInstance().saveAllDocuments();
86 final ApplyPatchDifferentiatedDialog dialog
= new ApplyPatchDifferentiatedDialog(project
, callback
);
90 public static void applySkipDirs(final List
<FilePatch
> patches
, final int skipDirs
) {
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
) {
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) {
123 Messages
.showErrorDialog(project
, VcsBundle
.message("patch.apply.error", patch
.getBeforeName(), ex
.getMessage()),
124 VcsBundle
.message("patch.apply.dialog.title"));
126 catch (Exception ex
) {
129 return ApplyPatchStatus
.FAILURE
;
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
);
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) {
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"));
154 if (status
!= ApplyPatchStatus
.ALREADY_APPLIED
) {
155 return showMergeDialog(project
, file
, threeTexts
.getBase(), threeTexts
.getPatched(), mergeRequestFactory
);
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
,
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
;
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
);
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
;
204 request
= DiffRequestFactory
.getInstance().create3WayDiffRequest(leftText
, rightText
, originalContent
, project
, null);
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()));