package org.erlide.engine;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.xtext.xbase.lib.Pair;
import org.erlide.engine.model.builder.ErlProblems;
import org.erlide.engine.model.builder.ProblemData;
import org.erlide.engine.model.erlang.IErlFunction;
import org.erlide.engine.model.erlang.ISourceRange;
import org.erlide.engine.model.root.IErlElementLocator;
import org.erlide.engine.model.root.IErlModule;
import org.erlide.engine.model.root.IErlProject;
import org.erlide.engine.util.ResourceUtil;
import org.erlide.util.ErlLogger;
import org.erlide.util.SystemConfiguration;
import org.erlide.util.erlang.OtpErlang;
import com.ericsson.otp.erlang.OtpErlangList;
import com.ericsson.otp.erlang.OtpErlangLong;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangRangeException;
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public final class MarkerUtils {
// do this to keep them from being detected as real tasks by Jenkins plugin
private static final String FIXME_TAG = "F" + "IXME";
private static final String XXX_TAG = "X" + "XX";
private static final String TODO_TAG = "T" + "ODO";
private static final String TODO_XXX_FIXME_PATTERN = "^[^%]*%+[ \t]*(" + TODO_TAG
+ "|" + XXX_TAG + "|" + FIXME_TAG + ").*";
// Copied from org.eclipse.ui.ide (since we don't want ui code in core)
public static final String PATH_ATTRIBUTE = "org.eclipse.ui.views.markers.path";//$NON-NLS-1$
private MarkerUtils() {
}
public static final String PROBLEM_MARKER = "org.erlide.core.problemmarker";
public static final String TASK_MARKER = "org.erlide.core.taskmarker";
public static void addTaskMarker(final IResource resource, final String message,
final int lineNumber, final int priority) {
createMarker(resource, null, message, lineNumber, IMarker.SEVERITY_INFO,
TASK_MARKER);
}
/**
* Add error markers from a list of error tuples
*
* @param resource
* @param errorList
*/
public static void addErrorMarkers(final IResource resource,
final OtpErlangList errorList) {
final OtpErlangObject[] messages = errorList.elements();
final Map<String, List<OtpErlangTuple>> groupedMessages = groupMessagesByFile(
messages);
for (final Entry<String, List<OtpErlangTuple>> entry : groupedMessages
.entrySet()) {
final String fileName = entry.getKey();
final IResource res = findResourceForFileName(resource, entry, fileName);
for (final OtpErlangTuple data : entry.getValue()) {
addAnnotationForMessage(resource, fileName, res, data);
}
}
}
private static IResource findResourceForFileName(final IResource resource,
final Entry<String, List<OtpErlangTuple>> entry, final String fileName) {
IResource res = resource;
if (!ResourceUtil.samePath(resource.getLocation().toString(), fileName)) {
final IProject project = resource.getProject();
res = ResourceUtil.findResourceByLocation(project, fileName);
if (res == null) {
try {
final IErlElementLocator model = ErlangEngine.getInstance()
.getModel();
final IErlProject erlProject = model.findProject(project);
if (erlProject != null) {
final IErlModule includeFile = model.findIncludeFromProject(
erlProject, fileName, fileName,
IErlElementLocator.Scope.REFERENCED_PROJECTS);
// ErlLogger.debug("inc::" + fileName + " "
// + resource.getName() + " "
// + erlProject.getName());
// ErlLogger.debug(" " + entry.getValue());
if (includeFile == null) {
res = resource;
} else {
res = includeFile.getResource();
}
} else {
res = resource;
}
} catch (final Exception e) {
ErlLogger.warn(e);
}
}
}
return res;
}
private static void addAnnotationForMessage(final IResource resource,
final String fileName, final IResource res, final OtpErlangTuple data) {
int line = 0;
if (data.elementAt(0) instanceof OtpErlangLong) {
try {
line = ((OtpErlangLong) data.elementAt(0)).intValue();
} catch (final OtpErlangRangeException e) {
}
}
int sev = IMarker.SEVERITY_INFO;
try {
switch (((OtpErlangLong) data.elementAt(3)).intValue()) {
case 0:
sev = IMarker.SEVERITY_ERROR;
break;
case 1:
sev = IMarker.SEVERITY_WARNING;
break;
default:
sev = IMarker.SEVERITY_INFO;
break;
}
} catch (final OtpErlangRangeException e) {
}
String msg = OtpErlang.asString(data.elementAt(2)).replaceAll("[\n\r]", " ");
if (msg.startsWith("-warning(")) {
msg = msg.substring("-warning(".length(), msg.length() - 2);
}
if (msg.startsWith("-error(")) {
msg = msg.substring("-error(".length(), msg.length() - 2);
}
if (msg.startsWith("\"")) {
msg = msg.substring(1, msg.length() - 1);
}
final IMarker marker = createMarker(res, fileName, msg, line, sev,
PROBLEM_MARKER);
if (marker != null) {
try {
marker.setAttribute(IMarker.SOURCE_ID, resource.getLocation().toString());
} catch (final CoreException e) {
}
}
}
private static Map<String, List<OtpErlangTuple>> groupMessagesByFile(
final OtpErlangObject[] messages) {
final Map<String, List<OtpErlangTuple>> result = Maps.newHashMap();
for (final OtpErlangObject msg : messages) {
final OtpErlangTuple tuple = (OtpErlangTuple) msg;
final String fileName = OtpErlang.asString(tuple.elementAt(1));
addMessage(result, fileName, tuple);
}
return result;
}
private static void addMessage(final Map<String, List<OtpErlangTuple>> map,
final String key, final OtpErlangTuple tuple) {
List<OtpErlangTuple> list = map.get(key);
if (list == null) {
list = Lists.newArrayList();
map.put(key, list);
}
list.add(tuple);
}
public static IMarker createProblemMarker(final IResource resource, final String path,
final String message, final int lineNumber, final int severity) {
return createMarker(resource, path, message, lineNumber, severity,
PROBLEM_MARKER);
}
public static IMarker[] getProblemsFor(final IResource resource) {
return getMarkersFor(resource, PROBLEM_MARKER);
}
public static IMarker[] getTasksFor(final IResource resource) {
return getMarkersFor(resource, TASK_MARKER);
}
private static IMarker[] getMarkersFor(final IResource resource, final String type) {
try {
if (resource != null && resource.exists()) {
return resource.findMarkers(type, false, IResource.DEPTH_INFINITE);
}
} catch (final CoreException e) {
// assume there are no tasks
}
return new IMarker[0];
}
public static void removeProblemMarkersFor(final IResource resource) {
removeMarkersFor(resource, PROBLEM_MARKER);
}
public static void removeTaskMarkers(final IResource resource) {
removeMarkersFor(resource, TASK_MARKER);
}
private static void removeMarkersFor(final IResource resource, final String type) {
try {
if (resource != null && resource.exists()) {
resource.deleteMarkers(type, true, IResource.DEPTH_INFINITE);
}
} catch (final CoreException e) {
// assume there were no problems
}
}
public static void deleteMarkers(final IResource resource) {
removeProblemMarkersFor(resource);
if (resource instanceof IFile) {
deleteMarkersWithCompiledFile(resource.getProject(), (IFile) resource);
// should we delete markers for dependent hrl files?
}
}
private static void deleteMarkersWithCompiledFile(final IProject project,
final IFile file) {
if (!project.isAccessible()) {
return;
}
try {
for (final IMarker m : project.findMarkers(PROBLEM_MARKER, true,
IResource.DEPTH_INFINITE)) {
final Object source_id = m.getAttribute(IMarker.SOURCE_ID);
if (source_id instanceof String
&& source_id.equals(file.getFullPath().toString())) {
try {
m.delete();
} catch (final CoreException e) {
// not much to do
}
}
}
} catch (final CoreException e) {
// not much to do
}
}
public void createProblemMarkerFor(final IResource resource,
final IErlFunction erlElement, final String message,
final int problemSeverity) throws CoreException {
final ISourceRange range = erlElement == null ? null : erlElement.getNameRange();
final IMarker marker = createProblemMarker(resource, null, message, 0,
problemSeverity);
final int start = range == null ? 0 : range.getOffset();
final int end = range == null ? 1 : start + range.getLength();
marker.setAttribute(IMarker.CHAR_START, Integer.valueOf(start));
marker.setAttribute(IMarker.CHAR_END, Integer.valueOf(end));
}
public static IMarker createSearchResultMarker(final IErlModule module,
final String type, final int offset, final int length) throws CoreException {
boolean setPath = false;
IResource resource = module.getCorrespondingResource();
if (resource == null) {
resource = ResourcesPlugin.getWorkspace().getRoot();
setPath = true;
}
final IMarker marker = resource.createMarker(type);
marker.setAttribute(IMarker.CHAR_START, offset);
marker.setAttribute(IMarker.CHAR_END, offset + length);
if (setPath) {
marker.setAttribute(PATH_ATTRIBUTE, module.getFilePath());
}
return marker;
}
public static IMarker createMarker(final IResource file, final String path,
final String message, final int lineNumber, final int severity,
final String markerKind) {
try {
IResource resource;
if (file != null) {
resource = file;
} else {
resource = ResourcesPlugin.getWorkspace().getRoot();
}
final IMarker marker = resource.createMarker(markerKind);
marker.setAttribute(IMarker.MESSAGE, message);
marker.setAttribute(IMarker.SEVERITY, severity);
marker.setAttribute(IMarker.LINE_NUMBER, lineNumber >= 0 ? lineNumber : 1);
marker.setAttribute(PATH_ATTRIBUTE, path);
if (path != null) {
marker.setAttribute(IMarker.SOURCE_ID, path);
} else {
marker.setAttribute(IMarker.SOURCE_ID, resource.getLocation().toString());
}
final ProblemData problem = ErlProblems.parse(message);
if (problem != null) {
marker.setAttribute(ProblemData.TAG, problem.getTag());
marker.setAttribute(ProblemData.ARGS,
Joiner.on('\0').join(problem.getMessageArgs(message)));
}
return marker;
} catch (final CoreException e) {
}
return null;
}
public static void createTaskMarkers(final IResource resource) {
if (SystemConfiguration.hasFeatureEnabled("erlide.skip.tasks")) {
return;
}
getScanMarkersFor(resource);
}
public static void getScanMarkersFor(final IResource resource) {
try (final BufferedReader reader = new BufferedReader(
new FileReader(resource.getLocation().toPortableString()))) {
String line = reader.readLine();
final List<Pair<String, Integer>> cl = new ArrayList<>();
int numline = 0;
while (line != null) {
if (line.matches(TODO_XXX_FIXME_PATTERN)) {
cl.add(new Pair<>(line, numline));
}
numline++;
line = reader.readLine();
}
for (final Pair<String, Integer> c : cl) {
createTaskMarkerAtText(resource, c.getValue(), c.getKey(), TODO_TAG,
IMarker.PRIORITY_NORMAL);
createTaskMarkerAtText(resource, c.getValue(), c.getKey(), XXX_TAG,
IMarker.PRIORITY_NORMAL);
createTaskMarkerAtText(resource, c.getValue(), c.getKey(), FIXME_TAG,
IMarker.PRIORITY_HIGH);
}
} catch (final IOException e) {
}
}
private static void createTaskMarkerAtText(final IResource resource, final int line,
final String text, final String tag, final int prio) {
if (text.contains(tag)) {
final int ix = text.indexOf(tag);
final String msg = text.substring(ix);
int dl = 0;
for (int i = 0; i < ix; i++) {
if (text.charAt(i) == '\n') {
dl++;
}
}
addTaskMarker(resource, msg, line + 1 + dl, prio);
}
}
}