// Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).
package com.twitter.intellij.pants.util;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Objects;
public class PantsOutputMessage {
private final int myStart;
private final int myEnd;
private final int myLineNumber;
private final String myFilePath;
private final Level myLevel;
public PantsOutputMessage(int start, int end, String filePath, int lineNumber) {
this(start, end, filePath, lineNumber, Level.INFO);
}
public PantsOutputMessage(int start, int end, String filePath, int lineNumber, Level level) {
myStart = start;
myEnd = end;
myFilePath = filePath;
myLineNumber = lineNumber;
myLevel = level;
}
public int getStart() {
return myStart;
}
public int getEnd() {
return myEnd;
}
public int getLineNumber() {
return myLineNumber;
}
@NotNull
public String getFilePath() {
return myFilePath;
}
@NotNull
public Level getLevel() {
return myLevel;
}
@Override
public String toString() {
return "PantsOutputMessage{" +
"start=" + myStart +
", end=" + myEnd +
", lineNumber=" + myLineNumber +
", filePath='" + myFilePath + '\'' +
'}';
}
@Nullable
public static PantsOutputMessage parseOutputMessage(@NotNull String message) {
return parseMessage(message, false, false);
}
@Nullable
public static PantsOutputMessage parseCompilerMessage(@NotNull String message) {
return parseMessage(message, true, true);
}
/**
* @param line the output
* @param onlyCompilerMessages will look only for compiler specific message e.g. with a log level
* @param checkFileExistence will check if the parsed {@code myFilePath} exists
*/
@Nullable
public static PantsOutputMessage parseMessage(@NotNull String line, boolean onlyCompilerMessages, boolean checkFileExistence) {
int i = 0;
final boolean isError = isError(line);
final boolean isWarning = isWarning(line);
if (isError || isWarning || line.contains("[debug]")) {
i = line.indexOf(']') + 1;
}
else if (onlyCompilerMessages) {
return null;
}
while (i < line.length() && (Character.isSpaceChar(line.charAt(i)) || line.charAt(i) == '\t')) {
++i;
}
final int start = i;
while (i < line.length() && line.charAt(i) != ' ' && line.charAt(i) != '\n' && line.charAt(i) != ':') {
++i;
}
int end = i;
i++;
final String filePath = line.substring(start, end);
if (checkFileExistence && !(new File(filePath).exists())) {
return null;
}
while (i < line.length() && Character.isDigit(line.charAt(i))) {
++i;
}
int lineNumber = 0;
try {
lineNumber = Integer.parseInt(line.substring(end + 1, i)) - 1;
end = i;
}
catch (Exception ignored) {
}
final Level level = isError ? Level.ERROR : isWarning ? Level.WARNING : Level.INFO;
return new PantsOutputMessage(start, end, filePath, lineNumber, level);
}
public static boolean isWarning(@NotNull String line) {
return containsLevel(line, "warning") || containsLevel(line, "warn");
}
public static boolean isError(@NotNull String line) {
return containsLevel(line, "error");
}
public static boolean containsLevel(@NotNull String line, @NotNull String level) {
return StringUtil.contains(line, "[" + level + "]") ||
StringUtil.contains(line, " " + level + ":");
}
public enum Level {
ERROR, WARNING, INFO
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PantsOutputMessage message = (PantsOutputMessage) o;
if (myEnd != message.myEnd) return false;
if (myLineNumber != message.myLineNumber) return false;
if (myStart != message.myStart) return false;
if (myFilePath != null ? !myFilePath.equals(message.myFilePath) : message.myFilePath != null) return false;
if (myLevel != message.myLevel) return false;
return true;
}
@Override
public int hashCode() {
return Objects.hash(
myStart,
myEnd,
myLineNumber,
myFilePath,
myLevel
);
}
}