package com.intellij.lang.javascript.flex.build;
import com.intellij.flex.FlexCommonUtils;
import com.intellij.flex.model.bc.OutputType;
import com.intellij.lang.javascript.flex.FlexUtils;
import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfiguration;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.util.text.StringTokenizer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.List;
public class ASC20CompilationTask extends FlexCompilationTask {
private Process myProcess;
private @Nullable String myPreviousUnreportedMessage;
public ASC20CompilationTask(final @NotNull Module module,
final @NotNull FlexBuildConfiguration bc,
final @NotNull Collection<FlexBuildConfiguration> dependencies) {
super(module, bc, dependencies);
}
protected void doStart(final FlexCompilationManager compilationManager) throws IOException {
final boolean app = myBC.getOutputType() != OutputType.Library;
final Sdk sdk = myBC.getSdk();
assert sdk != null;
final List<String> compilerCommand = FlexCompilationUtils.getASC20Command(myModule.getProject(), sdk, app);
final List<String> command = FlexCompilationUtils.buildCommand(compilerCommand, getConfigFiles(), myModule, myBC);
final ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.redirectErrorStream(true);
processBuilder.directory(new File(FlexUtils.getFlexCompilerWorkDirPath(myModule.getProject(), null)));
compilationManager.addMessage(this, CompilerMessageCategory.INFORMATION, StringUtil.join(command, " "), null, -1, -1);
myProcess = processBuilder.start();
readInputStream(compilationManager);
}
protected void doCancel() {
if (myProcess != null) {
myProcess.destroy();
}
}
private void readInputStream(final FlexCompilationManager compilationManager) {
ApplicationManager.getApplication().executeOnPooledThread(() -> {
final InputStreamReader reader = FlexCommonUtils.createInputStreamReader(myProcess.getInputStream());
try {
char[] buf = new char[2048];
int read;
while ((read = reader.read(buf, 0, buf.length)) >= 0) {
final String output = new String(buf, 0, read);
final StringTokenizer tokenizer = new StringTokenizer(output, "\r\n");
while (tokenizer.hasMoreElements()) {
final String message = tokenizer.nextElement();
if (StringUtil.isEmptyOrSpaces(message)) continue;
final boolean ok = handleCompilerMessage(compilationManager, message.trim());
if (!ok) {
myCompilationFailed = true;
}
}
}
printPreviousLine(compilationManager);
}
catch (IOException e) {
compilationManager.addMessage(this, CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1);
myCompilationFailed = true;
}
finally {
cancel();
try {
reader.close();
}
catch (IOException e) {/*ignore*/}
}
});
}
/**
* @return <code>false</code> if error reported
*/
private boolean handleCompilerMessage(final FlexCompilationManager compilationManager, final String message) {
if ("^".equals(message)) {
// ignore this and previous line - no need to print source code in Messages tool window
myPreviousUnreportedMessage = null;
return true;
}
if ("command line".equals(message)) {
// ignore this line and print previous if any
printPreviousLine(compilationManager);
return true;
}
if (message.startsWith("Exception in thread \"")) {
printPreviousLine(compilationManager);
compilationManager.addMessage(this, CompilerMessageCategory.ERROR, message, null, -1, -1);
return false;
}
// see messages_en.properties from Falcon sources
if (message.startsWith("Warning: ") ||
message.startsWith("Error: ") ||
message.startsWith("Syntax error: ") ||
message.startsWith("Internal error: ")) {
final CompilerMessageCategory category = message.startsWith("Warning: ") ? CompilerMessageCategory.WARNING
: CompilerMessageCategory.ERROR;
final int index = message.indexOf(": ");
final String usefulMessage = message.substring(index + ": ".length());
final Pair<String, Integer> sourcePathAndLine = FlexCommonUtils.getSourcePathAndLineFromASC20Message(myPreviousUnreportedMessage);
if (sourcePathAndLine == null) {
printPreviousLine(compilationManager);
compilationManager.addMessage(this, category, usefulMessage, null, -1, -1);
}
else {
if (!isNotSupportedOptionFromGeneratedConfig(usefulMessage, sourcePathAndLine.first)) {
compilationManager
.addMessage(this, category, usefulMessage, VfsUtilCore.pathToUrl(sourcePathAndLine.first), sourcePathAndLine.second, -1);
}
}
myPreviousUnreportedMessage = null;
return category == CompilerMessageCategory.WARNING;
}
printPreviousLine(compilationManager);
myPreviousUnreportedMessage = message;
return true;
}
private boolean isNotSupportedOptionFromGeneratedConfig(final String message, final String filePath) {
return filePath.equals(getConfigFiles().get(0).getPath())
&& ("'compiler.locale' is not fully supported.".equals(message) ||
"'compiler.theme' is not fully supported.".equals(message) ||
"'compiler.preloader' is not fully supported.".equals(message) ||
"'compiler.accessible' is not fully supported.".equals(message) ||
"'compiler.fonts.managers' is not fully supported.".equals(message) ||
"'static-link-runtime-shared-libraries' is not fully supported.".equals(message));
}
private void printPreviousLine(final FlexCompilationManager compilationManager) {
if (myPreviousUnreportedMessage != null) {
if (!myPreviousUnreportedMessage.equals("<theme />") &&
!myPreviousUnreportedMessage.equals("</locale>") &&
!myPreviousUnreportedMessage.equals("<preloader>spark.preloaders.SplashScreen</preloader>") &&
!myPreviousUnreportedMessage.equals("<accessible>true</accessible>") &&
!myPreviousUnreportedMessage.equals("<accessible>false</accessible>") &&
!myPreviousUnreportedMessage.equals("</managers>") &&
!myPreviousUnreportedMessage.equals("<static-link-runtime-shared-libraries>false</static-link-runtime-shared-libraries>")) {
compilationManager.addMessage(this, CompilerMessageCategory.INFORMATION, myPreviousUnreportedMessage, null, -1, -1);
}
myPreviousUnreportedMessage = null;
}
}
}