fix SafeDeleteTest
[idea/community.git] / plugins / hg4idea / src / org / zmlx / hg4idea / command / HgCommandService.java
1 // Copyright 2008-2010 Victor Iacoban
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software distributed under
10 // the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 // either express or implied. See the License for the specific language governing permissions and
12 // limitations under the License.
13 package org.zmlx.hg4idea.command;
14
15 import com.intellij.openapi.application.ApplicationManager;
16 import com.intellij.openapi.application.ModalityState;
17 import com.intellij.openapi.components.ServiceManager;
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.vfs.VirtualFile;
21 import com.intellij.vcsUtil.VcsUtil;
22 import org.jetbrains.annotations.Nullable;
23 import org.zmlx.hg4idea.*;
24
25 import javax.swing.*;
26 import java.awt.*;
27 import java.io.DataInputStream;
28 import java.io.DataOutputStream;
29 import java.io.File;
30 import java.io.IOException;
31 import java.net.Socket;
32 import java.nio.charset.Charset;
33 import java.util.Arrays;
34 import java.util.LinkedList;
35 import java.util.List;
36
37 public final class HgCommandService {
38   
39   private static File PROMPT_HOOKS_PLUGIN;
40
41   static final Logger LOG = Logger.getInstance(HgCommandService.class.getName());
42
43   private static final List<String> DEFAULT_OPTIONS = Arrays.asList(
44     "--config", "ui.merge=internal:merge"
45   );
46
47   private final Project myProject;
48   private final HgGlobalSettings mySettings;
49   private HgExecutableValidator validator;
50
51   public HgCommandService(Project project, HgGlobalSettings settings) {
52     myProject = project;
53     mySettings = settings;
54     if (PROMPT_HOOKS_PLUGIN == null) {
55       PROMPT_HOOKS_PLUGIN = HgUtil.getTemporaryPythonFile("prompthooks");
56     }
57     validator = new HgExecutableValidator(myProject);
58   }
59
60   public static HgCommandService getInstance(Project project) {
61     return ServiceManager.getService(project, HgCommandService.class);
62   }
63
64   @Nullable
65   HgCommandResult execute(VirtualFile repo, String operation, List<String> arguments) {
66     return execute(
67       repo, DEFAULT_OPTIONS, operation, arguments, Charset.defaultCharset()
68     );
69   }
70
71   @Nullable
72   HgCommandResult execute(VirtualFile repo, List<String> hgOptions,
73     String operation, List<String> arguments) {
74     return execute(repo, hgOptions, operation, arguments, Charset.defaultCharset());
75   }
76
77   @Nullable
78   HgCommandResult execute(VirtualFile repo, List<String> hgOptions,
79     String operation, List<String> arguments, Charset charset) {
80
81     if (!validator.check(mySettings)) {
82       return null;
83     }
84
85     List<String> cmdLine = new LinkedList<String>();
86     cmdLine.add(HgVcs.getInstance(myProject).getHgExecutable());
87     if (repo != null) {
88       cmdLine.add("--repository");
89       cmdLine.add(repo.getPath());
90     }
91
92     SocketServer promptServer = new SocketServer(new PromptReceiver());
93     WarningReceiver warningReceiver = new WarningReceiver();
94     SocketServer warningServer = new SocketServer(warningReceiver);
95     if (PROMPT_HOOKS_PLUGIN == null) {
96       throw new RuntimeException("Could not hook into the prompt mechanism of Mercurial");
97     }
98     try {
99       int promptPort = promptServer.start();
100       int warningPort = warningServer.start();
101       cmdLine.add("--config");
102       cmdLine.add("extensions.hg4ideapromptextension=" + PROMPT_HOOKS_PLUGIN.getAbsolutePath());
103       cmdLine.add("--config");
104       cmdLine.add("hg4ideaprompt.port=" + promptPort);
105       cmdLine.add("--config");
106       cmdLine.add("hg4ideawarn.port=" + warningPort);
107
108       // Other parts of the plugin count on the availability of the MQ extension, so make sure it is enabled
109       cmdLine.add("--config");
110       cmdLine.add("extensions.mq=");
111     } catch (IOException e) {
112       showError(e);
113       LOG.error("IOException during preparing command", e);
114       return null;
115     }
116     cmdLine.addAll(hgOptions);
117     cmdLine.add(operation);
118     if (arguments != null && arguments.size() != 0) {
119       cmdLine.addAll(arguments);
120     }
121     ShellCommand shellCommand = new ShellCommand();
122     HgCommandResult result;
123     try {
124       LOG.debug(cmdLine.toString());
125       String workingDir = repo != null ? repo.getPath() : null;
126       result = shellCommand.execute(cmdLine, workingDir, charset);
127     } catch (ShellCommandException e) {
128       showError(e);
129       LOG.error(e.getMessage(), e);
130       return null;
131     } finally {
132       promptServer.stop();
133       warningServer.stop();
134     }
135     String warnings = warningReceiver.getWarnings();
136     result.setWarnings(warnings);
137     return result;
138
139   }
140
141   private void showError(Exception e) {
142     StringBuilder message = new StringBuilder();
143     message.append(HgVcsMessages.message("hg4idea.command.executable.error",
144       HgVcs.getInstance(myProject).getHgExecutable()))
145       .append("\n")
146       .append("Original Error:\n")
147       .append(e.getMessage());
148
149     VcsUtil.showErrorMessage(
150       myProject,
151       message.toString(),
152       HgVcsMessages.message("hg4idea.error")
153     );
154   }
155
156   private static class WarningReceiver extends SocketServer.Protocol{
157     private StringBuffer warnings = new StringBuffer();
158
159     public boolean handleConnection(Socket socket) throws IOException {
160       DataInputStream dataInput = new DataInputStream(socket.getInputStream());
161
162       int numOfWarnings = dataInput.readInt();
163       for (int i = 0; i < numOfWarnings; i++) {
164         warnings.append(new String(readDataBlock(dataInput)));
165       }
166       return true;
167     }
168
169
170     public String getWarnings() {
171       return warnings.toString();
172     }
173   }
174
175   private static class PromptReceiver extends SocketServer.Protocol {
176
177     public boolean handleConnection(Socket socket) throws IOException {
178       DataInputStream dataInput = new DataInputStream(socket.getInputStream());
179       final String message = new String(readDataBlock(dataInput));
180       int numOfChoices = dataInput.readInt();
181       final Choice[] choices = new Choice[numOfChoices];
182       for (int i = 0; i < numOfChoices; i++) {
183         String choice = new String(readDataBlock(dataInput));
184         choices[i] = new Choice(choice);
185       }
186       int defaultChoiceInt = dataInput.readInt();
187       final Choice defaultChoice = choices[defaultChoiceInt];
188
189       final int[] index = new int[]{-1};
190       ApplicationManager.getApplication().invokeAndWait(new Runnable() {
191         public void run() {
192           Window parent = ApplicationManager.getApplication().getComponent(Window.class);
193           index[0] = JOptionPane.showOptionDialog(
194             parent,
195             message,
196             "hg4idea",
197             JOptionPane.OK_CANCEL_OPTION,
198             JOptionPane.QUESTION_MESSAGE,
199             null,
200             choices,
201             defaultChoice);
202         }
203       }, ModalityState.defaultModalityState());
204
205       int chosen = index[0];
206       DataOutputStream out = new DataOutputStream(socket.getOutputStream());
207       if (chosen == JOptionPane.CLOSED_OPTION) {
208         out.writeInt(-1);
209       } else {
210         out.writeInt(chosen);
211       }
212       return true;
213     }
214
215     private static class Choice{
216       private final String fullString;
217       private final String representation;
218       private final String choiceChar;
219
220       private Choice(String fullString) {
221         this.fullString = fullString;
222         this.representation = fullString.replaceAll("&", "");
223         int index = fullString.indexOf("&");
224         this.choiceChar = "" + fullString.charAt(index + 1);
225         
226       }
227
228       @Override
229       public String toString() {
230         return representation;
231       }
232
233       @Override
234       public boolean equals(Object o) {
235         if (this == o) return true;
236         if (o == null || getClass() != o.getClass()) return false;
237
238         Choice choice = (Choice) o;
239
240         if (!fullString.equals(choice.fullString)) return false;
241
242         return true;
243       }
244
245       @Override
246       public int hashCode() {
247         return fullString.hashCode();
248       }
249     }
250   }
251
252
253 }