blob: be7db2e355b56c2e9a520099364c3879518ce378 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.compiler.impl.javaCompiler.javac;
import com.intellij.compiler.OutputParser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompilerBundle;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.rt.compiler.JavacResourcesReader;
import com.intellij.util.StringBuilderSpinAllocator;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JavacOutputParser extends OutputParser {
private final int myTabSize;
@NonNls private String WARNING_PREFIX = "warning:"; // default value
public JavacOutputParser(Project project) {
myTabSize = CodeStyleSettingsManager.getSettings(project).getTabSize(StdFileTypes.JAVA);
if (ApplicationManager.getApplication().isUnitTestMode()) {
// emulate patterns setup if 'embedded' javac is used (javac is started not via JavacRunner)
addJavacPattern(JavacResourcesReader.MSG_PARSING_STARTED + JavacResourcesReader.CATEGORY_VALUE_DIVIDER + "[parsing started {0}]");
addJavacPattern(JavacResourcesReader.MSG_PARSING_COMPLETED + JavacResourcesReader.CATEGORY_VALUE_DIVIDER + "[parsing completed {0}ms]");
addJavacPattern(JavacResourcesReader.MSG_LOADING + JavacResourcesReader.CATEGORY_VALUE_DIVIDER + "[loading {0}]");
addJavacPattern(JavacResourcesReader.MSG_CHECKING + JavacResourcesReader.CATEGORY_VALUE_DIVIDER + "[checking {0}]");
addJavacPattern(JavacResourcesReader.MSG_WROTE + JavacResourcesReader.CATEGORY_VALUE_DIVIDER + "[wrote {0}]");
}
}
public boolean processMessageLine(Callback callback) {
if (super.processMessageLine(callback)) {
return true;
}
final String line = callback.getCurrentLine();
if (line == null) {
return false;
}
if (JavacResourcesReader.MSG_PATTERNS_START.equals(line)) {
myParserActions.clear();
while (true) {
final String patternLine = callback.getNextLine();
if (JavacResourcesReader.MSG_PATTERNS_END.equals(patternLine)) {
break;
}
addJavacPattern(patternLine);
}
return true;
}
int colonIndex1 = line.indexOf(':');
if (colonIndex1 == 1){ // drive letter
colonIndex1 = line.indexOf(':', colonIndex1 + 1);
}
if (colonIndex1 >= 0){ // looks like found something like file path
@NonNls String part1 = line.substring(0, colonIndex1).trim();
if(part1.equalsIgnoreCase("error") /*jikes*/ || part1.equalsIgnoreCase("Caused by")) {
addMessage(callback, CompilerMessageCategory.ERROR, line.substring(colonIndex1));
return true;
}
if(part1.equalsIgnoreCase("warning")) {
addMessage(callback, CompilerMessageCategory.WARNING, line.substring(colonIndex1));
return true;
}
if(part1.equals("javac")) {
addMessage(callback, CompilerMessageCategory.ERROR, line);
return true;
}
final int colonIndex2 = line.indexOf(':', colonIndex1 + 1);
if (colonIndex2 >= 0){
final String filePath = part1.replace(File.separatorChar, '/');
final Boolean fileExists = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>(){
public Boolean compute(){
return LocalFileSystem.getInstance().findFileByPath(filePath) != null? Boolean.TRUE : Boolean.FALSE;
}
});
if (!fileExists.booleanValue()) {
// the part one turned out to be something else than a file path
return true;
}
try {
final int lineNum = Integer.parseInt(line.substring(colonIndex1 + 1, colonIndex2).trim());
String message = line.substring(colonIndex2 + 1).trim();
CompilerMessageCategory category = CompilerMessageCategory.ERROR;
if (message.startsWith(WARNING_PREFIX)){
message = message.substring(WARNING_PREFIX.length()).trim();
category = CompilerMessageCategory.WARNING;
}
List<String> messages = new ArrayList<String>();
messages.add(message);
int colNum;
String prevLine = null;
do{
final String nextLine = callback.getNextLine();
if (nextLine == null) {
return false;
}
if (nextLine.trim().equals("^")){
final CharSequence chars = prevLine == null ? line : prevLine;
final int offset = Math.max(0, Math.min(chars.length(), nextLine.indexOf('^')));
colNum = EditorUtil.calcColumnNumber(null, chars,0, offset, myTabSize);
String messageEnd = callback.getNextLine();
while (isMessageEnd(messageEnd)) {
messages.add(messageEnd.trim());
messageEnd = callback.getNextLine();
}
if (messageEnd != null) {
callback.pushBack(messageEnd);
}
break;
}
if (prevLine != null) {
messages.add(prevLine);
}
prevLine = nextLine;
}
while(true);
if (colNum >= 0){
messages = convertMessages(messages);
final StringBuilder buf = StringBuilderSpinAllocator.alloc();
try {
for (final String m : messages) {
if (buf.length() > 0) {
buf.append("\n");
}
buf.append(m);
}
addMessage(callback, category, buf.toString(), VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, filePath), lineNum, colNum + 1);
}
finally {
StringBuilderSpinAllocator.dispose(buf);
}
return true;
}
}
catch (NumberFormatException ignored) {
}
}
}
if(line.endsWith("java.lang.OutOfMemoryError")) {
addMessage(callback, CompilerMessageCategory.ERROR, CompilerBundle.message("error.javac.out.of.memory"));
return true;
}
addMessage(callback, CompilerMessageCategory.INFORMATION, line);
return true;
}
private static boolean isMessageEnd(String line) {
return line != null && line.length() > 0 && Character.isWhitespace(line.charAt(0));
}
private static List<String> convertMessages(List<String> messages) {
if(messages.size() <= 1) {
return messages;
}
final String line0 = messages.get(0);
final String line1 = messages.get(1);
final int colonIndex = line1.indexOf(':');
if (colonIndex > 0){
@NonNls String part1 = line1.substring(0, colonIndex).trim();
// jikes
if ("symbol".equals(part1)){
String symbol = line1.substring(colonIndex + 1).trim();
messages.remove(1);
if(messages.size() >= 2) {
messages.remove(1);
}
messages.set(0, line0 + " " + symbol);
}
}
return messages;
}
private void addJavacPattern(@NonNls final String line) {
final int dividerIndex = line.indexOf(JavacResourcesReader.CATEGORY_VALUE_DIVIDER);
if (dividerIndex < 0) {
// by reports it may happen for some IBM JDKs (empty string?)
return;
}
final String category = line.substring(0, dividerIndex);
final String resourceBundleValue = line.substring(dividerIndex + 1);
if (JavacResourcesReader.MSG_PARSING_COMPLETED.equals(category) ||
JavacResourcesReader.MSG_PARSING_STARTED.equals(category) ||
JavacResourcesReader.MSG_WROTE.equals(category)
) {
myParserActions.add(new FilePathActionJavac(createMatcher(resourceBundleValue)));
}
else if (JavacResourcesReader.MSG_CHECKING.equals(category)) {
myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) {
protected void doExecute(final String line, String parsedData, final Callback callback) {
callback.setProgressText(CompilerBundle.message("progress.compiling.class", parsedData));
}
});
}
else if (JavacResourcesReader.MSG_LOADING.equals(category)) {
myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) {
protected void doExecute(final String line, @Nullable String parsedData, final Callback callback) {
callback.setProgressText(CompilerBundle.message("progress.loading.classes"));
}
});
}
else if (JavacResourcesReader.MSG_NOTE.equals(category)) {
myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) {
protected void doExecute(final String line, @Nullable final String filePath, final Callback callback) {
final boolean fileExists = filePath != null && ApplicationManager.getApplication().runReadAction(new Computable<Boolean>(){
public Boolean compute(){
return LocalFileSystem.getInstance().findFileByPath(filePath) != null? Boolean.TRUE : Boolean.FALSE;
}
});
if (fileExists) {
addMessage(callback, CompilerMessageCategory.WARNING, line, VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, filePath), -1, -1);
}
else {
addMessage(callback, CompilerMessageCategory.INFORMATION, line);
}
}
});
}
else if (JavacResourcesReader.MSG_WARNING.equals(category)) {
WARNING_PREFIX = resourceBundleValue;
}
else if (JavacResourcesReader.MSG_STATISTICS.equals(category)) {
myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) {
protected void doExecute(final String line, @Nullable String parsedData, final Callback callback) {
// empty
}
});
}
else if (JavacResourcesReader.MSG_IGNORED.equals(category)) {
myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) {
protected void doExecute(final String line, @Nullable String parsedData, final Callback callback) {
// ignored
}
});
}
}
/**
* made public for Tests, do not use this method directly
*/
public static Matcher createMatcher(@NonNls final String resourceBundleValue) {
@NonNls String regexp = resourceBundleValue.replaceAll("([\\[\\]\\(\\)\\.\\*])", "\\\\$1");
regexp = regexp.replaceAll("\\{\\d+\\}", "(.+)");
return Pattern.compile(regexp, Pattern.CASE_INSENSITIVE).matcher("");
}
}