remove some more duplication
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / incremental / groovy / GroovycOSProcessHandler.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package org.jetbrains.jps.incremental.groovy;
18
19 import com.intellij.execution.process.BaseOSProcessHandler;
20 import com.intellij.execution.process.ProcessOutputTypes;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.util.Key;
23 import com.intellij.openapi.util.io.FileUtil;
24 import com.intellij.openapi.util.text.StringUtil;
25 import com.intellij.util.Consumer;
26 import com.intellij.util.containers.ContainerUtil;
27 import org.jetbrains.annotations.Nullable;
28 import org.jetbrains.groovy.compiler.rt.GroovycRunner;
29 import org.jetbrains.jps.incremental.messages.BuildMessage;
30 import org.jetbrains.jps.incremental.messages.CompilerMessage;
31
32 import java.io.*;
33 import java.util.*;
34
35 /**
36  * @author: Dmitry.Krasilschikov
37  * @date: 16.04.2007
38  */
39 public class GroovycOSProcessHandler extends BaseOSProcessHandler {
40   public static final String GROOVY_COMPILER_IN_OPERATION = "Groovy compiler in operation...";
41   private final List<OutputItem> myCompiledItems = new ArrayList<OutputItem>();
42   private final Set<File> toRecompileFiles = new HashSet<File>();
43   private final List<CompilerMessage> compilerMessages = new ArrayList<CompilerMessage>();
44   private final StringBuffer stdErr = new StringBuffer();
45
46   private static final Logger LOG = Logger.getInstance("org.jetbrains.jps.incremental.groovy.GroovycOSProcessHandler");
47   private final Consumer<String> myStatusUpdater;
48
49   private GroovycOSProcessHandler(Process process, Consumer<String> statusUpdater) {
50     super(process, null, null);
51     myStatusUpdater = statusUpdater;
52   }
53
54   public void notifyTextAvailable(final String text, final Key outputType) {
55     super.notifyTextAvailable(text, outputType);
56
57     if (LOG.isDebugEnabled()) {
58       LOG.debug("Received from groovyc: " + text);
59     }
60
61     if (outputType == ProcessOutputTypes.SYSTEM) {
62       return;
63     }
64
65     if (outputType == ProcessOutputTypes.STDERR) {
66       stdErr.append(StringUtil.convertLineSeparators(text));
67       return;
68     }
69
70
71     parseOutput(text);
72   }
73
74   private final StringBuffer outputBuffer = new StringBuffer();
75
76   protected void updateStatus(@Nullable String status) {
77     myStatusUpdater.consume(status == null ? GROOVY_COMPILER_IN_OPERATION : status);
78   }
79
80   private void parseOutput(String text) {
81     final String trimmed = text.trim();
82
83     if (trimmed.startsWith(GroovycRunner.PRESENTABLE_MESSAGE)) {
84       updateStatus(trimmed.substring(GroovycRunner.PRESENTABLE_MESSAGE.length()));
85       return;
86     }
87
88     if (GroovycRunner.CLEAR_PRESENTABLE.equals(trimmed)) {
89       updateStatus(null);
90       return;
91     }
92
93
94     if (StringUtil.isNotEmpty(text)) {
95       outputBuffer.append(text);
96
97       //compiled start marker have to be in the beginning on each string
98       if (outputBuffer.indexOf(GroovycRunner.COMPILED_START) != -1) {
99         if (outputBuffer.indexOf(GroovycRunner.COMPILED_END) == -1) {
100           return;
101         }
102
103         final String compiled = handleOutputBuffer(GroovycRunner.COMPILED_START, GroovycRunner.COMPILED_END);
104         final List<String> list = StringUtil.split(compiled, GroovycRunner.SEPARATOR);
105         String outputPath = list.get(0);
106         String sourceFile = list.get(1);
107
108         ContainerUtil.addIfNotNull(getOutputItem(outputPath, sourceFile), myCompiledItems);
109
110       }
111       else if (outputBuffer.indexOf(GroovycRunner.TO_RECOMPILE_START) != -1) {
112         if (outputBuffer.indexOf(GroovycRunner.TO_RECOMPILE_END) != -1) {
113           String url = handleOutputBuffer(GroovycRunner.TO_RECOMPILE_START, GroovycRunner.TO_RECOMPILE_END);
114           toRecompileFiles.add(new File(url));
115         }
116       }
117       else if (outputBuffer.indexOf(GroovycRunner.MESSAGES_START) != -1) {
118         if (!(outputBuffer.indexOf(GroovycRunner.MESSAGES_END) != -1)) {
119           return;
120         }
121
122         text = handleOutputBuffer(GroovycRunner.MESSAGES_START, GroovycRunner.MESSAGES_END);
123
124         List<String> tokens = StringUtil.split(text, GroovycRunner.SEPARATOR);
125         LOG.assertTrue(tokens.size() > 4, "Wrong number of output params");
126
127         String category = tokens.get(0);
128         String message = tokens.get(1);
129         String url = tokens.get(2);
130         String lineNum = tokens.get(3);
131         String columnNum = tokens.get(4);
132
133         int lineInt;
134         int columnInt;
135
136         try {
137           lineInt = Integer.parseInt(lineNum);
138           columnInt = Integer.parseInt(columnNum);
139         }
140         catch (NumberFormatException e) {
141           LOG.error(e);
142           lineInt = 0;
143           columnInt = 0;
144         }
145
146         BuildMessage.Kind kind = category.equals(org.jetbrains.groovy.compiler.rt.CompilerMessage.ERROR)
147                                  ? BuildMessage.Kind.ERROR
148                                  : category.equals(org.jetbrains.groovy.compiler.rt.CompilerMessage.WARNING)
149                                    ? BuildMessage.Kind.WARNING
150                                    : BuildMessage.Kind.INFO;
151
152         compilerMessages.add(new CompilerMessage("Groovyc", kind, message, url, -1, -1, -1, lineInt, columnInt));
153       }
154     }
155   }
156
157   private String handleOutputBuffer(String startMarker, String endMarker) {
158     final int start = outputBuffer.indexOf(startMarker);
159     final int end = outputBuffer.indexOf(endMarker);
160     if (start > end) {
161       throw new AssertionError("Malformed Groovyc output: " + outputBuffer.toString());
162     }
163
164     String text = outputBuffer.substring(start + startMarker.length(), end);
165
166     outputBuffer.delete(start, end + endMarker.length());
167
168     return text.trim();
169   }
170
171   @Nullable
172   private static OutputItem getOutputItem(final String outputPath, final String sourceFile) {
173     return new OutputItem(outputPath, sourceFile);
174   }
175
176   public List<OutputItem> getSuccessfullyCompiled() {
177     return myCompiledItems;
178   }
179
180   public Set<File> getToRecompileFiles() {
181     return toRecompileFiles;
182   }
183
184   public List<CompilerMessage> getCompilerMessages() {
185     ArrayList<CompilerMessage> messages = new ArrayList<CompilerMessage>(compilerMessages);
186     final StringBuffer unparsedBuffer = getStdErr();
187     if (unparsedBuffer.length() != 0) {
188       messages.add(new CompilerMessage("Groovyc", BuildMessage.Kind.INFO, unparsedBuffer.toString()));
189     }
190
191     final int exitValue = getProcess().exitValue();
192     if (messages.isEmpty() && exitValue != 0) {
193       messages.add(new CompilerMessage("Groovyc", BuildMessage.Kind.ERROR, "Internal groovyc error: code " + exitValue));
194     }
195
196     return messages;
197   }
198
199   public StringBuffer getStdErr() {
200     return stdErr;
201   }
202
203   public static File fillFileWithGroovycParameters(final String outputDir,
204                                                    final Collection<String> changedSources,
205                                                    String finalOutput,
206                                                    Map<String, String> class2Src, @Nullable final String encoding, List<String> patchers) throws IOException {
207     File tempFile = FileUtil.createTempFile("ideaGroovyToCompile", ".txt", true);
208
209     final Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(tempFile)));
210     try {
211       for (String file : changedSources) {
212         writer.write(GroovycRunner.SRC_FILE + "\n");
213         writer.write(file);
214         writer.write("\n");
215       }
216
217       writer.write("class2src\n");
218       for (Map.Entry<String, String> entry : class2Src.entrySet()) {
219         writer.write(entry.getKey() + "\n");
220         writer.write(entry.getValue() + "\n");
221       }
222       writer.write(GroovycRunner.END + "\n");
223
224       writer.write(GroovycRunner.PATCHERS + "\n");
225       for (String patcher : patchers) {
226         writer.write(patcher + "\n");
227       }
228       writer.write(GroovycRunner.END + "\n");
229       if (encoding != null) {
230         writer.write(GroovycRunner.ENCODING + "\n");
231         writer.write(encoding + "\n");
232       }
233       writer.write(GroovycRunner.OUTPUTPATH + "\n");
234       writer.write(outputDir);
235       writer.write("\n");
236       writer.write(GroovycRunner.FINAL_OUTPUTPATH + "\n");
237       writer.write(finalOutput);
238       writer.write("\n");
239     }
240     finally {
241       writer.close();
242     }
243     return tempFile;
244   }
245
246   public static GroovycOSProcessHandler runGroovyc(Process process, Consumer<String> updater) {
247     GroovycOSProcessHandler processHandler = new GroovycOSProcessHandler(process, updater);
248
249     processHandler.startNotify();
250     processHandler.waitFor();
251     return processHandler;
252   }
253
254   public static class OutputItem {
255     public final String outputPath;
256     public final String sourcePath;
257
258     public OutputItem(String outputPath, String sourceFileName) {
259       this.outputPath = outputPath;
260       sourcePath = sourceFileName;
261     }
262   }
263
264 }