blob: c217bfa2a002aaf631f4441b2360a8182c90d5d3 [file] [log] [blame]
/********************************************************************
* Copyright (c) 2007 Contributors. All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0
* which accompanies this distribution and is available at
* http://eclipse.org/legal/epl-v10.html
*
* Contributors: IBM Corporation - initial API and implementation
* Helen Hawkins - initial version (bug 148190)
*******************************************************************/
package org.eclipse.ajdt.internal.ui.ajde;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.aspectj.ajde.core.IBuildMessageHandler;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessage.Kind;
import org.aspectj.bridge.ISourceLocation;
import org.eclipse.ajdt.core.AJLog;
import org.eclipse.ajdt.core.AspectJPlugin;
import org.eclipse.ajdt.internal.core.ajde.CoreCompilerConfiguration;
import org.eclipse.ajdt.internal.core.ajde.FileURICache;
import org.eclipse.ajdt.internal.ui.editor.AspectJEditor;
import org.eclipse.ajdt.internal.ui.preferences.AspectJPreferences;
import org.eclipse.ajdt.internal.ui.text.UIMessages;
import org.eclipse.ajdt.internal.ui.tracing.DebugTracing;
import org.eclipse.ajdt.internal.utils.AJDTUtils;
import org.eclipse.ajdt.ui.AspectJUIPlugin;
import org.eclipse.ajdt.ui.IAJModelMarker;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaModelManager;
/**
* IBuildMessageHandler implementation which records warnings in the Problems
* View. All Errors with stack traces and ABORT's are displayed in an error
* dialog. By default it ignores INFO messages and checks whether the user
* has selected to ignore WEAVEINFO messages.
*/
public class UIMessageHandler implements IBuildMessageHandler {
// --------------- impl on top of IMessageHandler ----------
/**
* resources that were affected by the compilation.
*/
private static Set<IResource> affectedResources = new HashSet<IResource>();
/**
* Markers created in projects other than the one under compilation, which
* should be cleared next time the compiled project is rebuilt
*/
private static Map<String, List<IMarker>> otherProjectMarkers = new HashMap<String, List<IMarker>>();
/**
* Indicates whether the most recent build was full or incremental
*/
private static boolean lastBuildWasFull;
private List<Kind> ignoring;
private List<ProblemTracker> problems = new ArrayList<ProblemTracker>();
public UIMessageHandler(IProject project) {
ignoring = new ArrayList<Kind>();
if (!AspectJPreferences.getBooleanPrefValue(project, AspectJPreferences.OPTION_verbose)) {
ignore(IMessage.INFO);
}
if (!AspectJPreferences.getShowWeaveMessagesOption(project)) {
ignore(IMessage.WEAVEINFO);
}
}
public boolean handleMessage(IMessage message) {
IMessage.Kind kind = message.getKind();
if (kind == IMessage.ABORT || message.getThrown() != null) {
// an exception has been thrown by AspectJ, therefore
// want to create an error dialog containing the information
// and display it to the user
AJDTErrorHandler.handleInternalError(UIMessages.ajErrorDialogTitle,
message.getMessage(), message.getThrown());
return true;
}
if (isIgnoring(kind)) {
return true;
}
if (message.getSourceLocation() == null) {
AJLog.log(AJLog.COMPILER_MESSAGES, message.getMessage()); //$NON-NLS-1$
problems.add(new ProblemTracker(message.getMessage(),
null,message.getKind()));
} else {
if (DebugTracing.DEBUG_COMPILER_MESSAGES) {
// avoid constructing log string if trace is not active
AJLog.log(AJLog.COMPILER_MESSAGES, "addSourcelineTask message=" //$NON-NLS-1$
+ message.getMessage() + " file=" //$NON-NLS-1$
+ message.getSourceLocation().getSourceFile().getPath()
+ " line=" + message.getSourceLocation().getLine()); //$NON-NLS-1$
} else {
AJLog.log(AJLog.COMPILER_MESSAGES,message.getMessage());
}
problems.add(new ProblemTracker(message.getMessage(),
message.getSourceLocation(),
message.getKind(),
message.getDeclared(),
message.getExtraSourceLocations(),
message.getID(),
message.getSourceStart(),
message.getSourceEnd(),
message.getThrown()));
}
return true;
}
public void dontIgnore(Kind kind) {
if (null != kind) {
ignoring.remove(kind);
}
}
public boolean isIgnoring(Kind kind) {
return ((null != kind) && (ignoring.contains(kind)));
}
public void ignore(Kind kind) {
if ((null != kind) && (!ignoring.contains(kind))) {
ignoring.add(kind);
}
}
// --------------- impl on top of IMessageHandler ----------
protected void addAffectedResource(IResource res) {
affectedResources.add(res);
}
/**
* Inner class used to track problems found during compilation Values of -1
* are used to indicate no line or column number available.
*/
static class ProblemTracker {
public ISourceLocation location;
public String message;
public IMessage.Kind kind;
public boolean declaredErrorOrWarning = false;
public List<ISourceLocation> extraLocs;
public Throwable thrown;
public int id;
public int start;
public int end;
public ProblemTracker(String m, ISourceLocation l, IMessage.Kind k) {
this(m, l, k, false, null, -1, -1, -1,null);
}
public ProblemTracker(String m, ISourceLocation l, IMessage.Kind k,
boolean deow, List<ISourceLocation> extraLocs, int id,
int start, int end, Throwable thrown) {
location = l;
message = m;
kind = k;
declaredErrorOrWarning = deow;
this.extraLocs = extraLocs;
this.id = id;
this.start = start;
this.end = end;
this.thrown = thrown;
}
}
public List<ProblemTracker> getErrors() {
List<ProblemTracker> errors = new ArrayList<ProblemTracker>();
for (Iterator<ProblemTracker> iter = problems.iterator(); iter.hasNext();) {
ProblemTracker prob = (ProblemTracker) iter.next();
if (prob.kind.equals(IMessage.ERROR)) {
errors.add(prob);
}
}
return errors;
}
/**
* Callable from anywhere in the plugin, will put any unreported problems
* onto the task bar. This is currently used by the model builder for build
* configuration files. Ajde builds the model and reports errors through
* this class - BuildConfigurationEditor then asks this helper method to
* report them. We need to move this error reporting stuff out of here if it
* is going to be used by more than just the compiler.
*/
public void showOutstandingProblems(IProject project) {
if (problems.size() > 0 || affectedResources.size() > 0) {
showMessages(project);
}
}
private void showMessages(final IProject project) {
// THIS MUST STAY IN A SEPARATE THREAD - This is because we need
// to create and setup the marker in an atomic operation. See
// AMC or ASC.
IWorkspaceRunnable r = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) {
try {
Iterator<IResource> affectedResourceIterator = affectedResources
.iterator();
AJLog.log(AJLog.COMPILER,"Types affected during build = "+affectedResources.size()); //$NON-NLS-1$
IResource ir = null;
while (affectedResourceIterator.hasNext()) {
ir = (IResource) affectedResourceIterator.next();
try {
if (ir.exists()) {
ir.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false,
IResource.DEPTH_INFINITE);
ir.deleteMarkers(IAJModelMarker.AJDT_PROBLEM_MARKER, true,
IResource.DEPTH_INFINITE);
ir.deleteMarkers(IMarker.TASK, true,
IResource.DEPTH_INFINITE);
// now removed markers from compilation participants
HashSet<String> managedMarkers = JavaModelManager.getJavaModelManager().compilationParticipants.managedMarkerTypes();
for (String managedMarker : managedMarkers) {
ir.deleteMarkers(managedMarker, true, IResource.DEPTH_INFINITE);
}
}
} catch (CoreException re) {
AJLog.log("Failed marker deletion: resource=" //$NON-NLS-1$
+ ir.getLocation());
throw re;
}
}
Iterator<ProblemTracker> problemIterator = problems.iterator();
ProblemTracker p = null;
while (problemIterator.hasNext()) {
p = (ProblemTracker) problemIterator.next();
ir = null;
IMarker marker = null;
try {
if (p.location != null) {
ir = locationToResource(p.location, project);
if ((ir != null) && ir.exists()) {
// 128803 - only add problems to affected resources
if (lastBuildWasFull
|| affectedResources.contains(ir)
|| ir.getProject() != project) {
int prio = getTaskPriority(p);
if (prio != -1) {
marker = ir.createMarker(IMarker.TASK);
marker.setAttribute(IMarker.PRIORITY, prio);
} else {
if (p.declaredErrorOrWarning) {
marker = ir.createMarker(IAJModelMarker.AJDT_PROBLEM_MARKER);
} else {
// create Java marker with problem id so
// that quick fix is available
marker = ir.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
marker.setAttribute(IJavaModelMarker.ID,p.id);
}
}
if ((p.start >= 0) && (p.end >= 0)) {
marker.setAttribute(IMarker.CHAR_START,new Integer(p.start));
marker.setAttribute(IMarker.CHAR_END,new Integer(p.end + 1));
}
if (!ir.getProject().equals(project)) {
addOtherProjectMarker(project,marker);
}
if (p.location.getLine() > 0) {
marker.setAttribute(IMarker.LINE_NUMBER,
new Integer(p.location.getLine()));
}
} else {
AJLog.log(AJLog.COMPILER_MESSAGES,
"Not adding marker for problem because it's " //$NON-NLS-1$
+ "against a resource which is not in the list of affected resources" //$NON-NLS-1$
+ " provided by the compiler. Resource=" + ir + " Problem message=" //$NON-NLS-1$ //$NON-NLS-2$
+ p.message + " line=" + p.location.getLine()); //$NON-NLS-1$
}
}
} else {
marker = project.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
}
if(marker != null) {
setSeverity(marker, p.kind);
if ((p.extraLocs != null) && (p.extraLocs.size() > 0)) { // multiple
// part
// message
int relCount=0;
for (Iterator<?> iter = p.extraLocs.iterator(); iter
.hasNext();) {
ISourceLocation sLoc = (ISourceLocation) iter
.next();
StringBuffer attrData = new StringBuffer();
attrData.append(sLoc.getSourceFile().getAbsolutePath());
attrData.append(":::"); //$NON-NLS-1$
attrData.append(sLoc.getLine());
attrData.append(":::"); //$NON-NLS-1$
attrData.append(sLoc.getEndLine());
attrData.append(":::"); //$NON-NLS-1$
attrData.append(sLoc.getColumn());
marker.setAttribute(AspectJUIPlugin.RELATED_LOCATIONS_ATTRIBUTE_PREFIX
+(relCount++),attrData.toString());
}
}
setMessage(marker, p.message);
}
} catch (CoreException re) {
AJLog.log("Failed marker creation: resource=" //$NON-NLS-1$
+ p.location.getSourceFile()
.getPath()
+ " line=" //$NON-NLS-1$
+ p.location.getLine()
+ " message=" + p.message); //$NON-NLS-1$
throw re;
}
}
clearMessages();
} catch (CoreException e) {
AJDTErrorHandler.handleAJDTError(
UIMessages.CompilerTaskListManager_Error_creating_marker, e);
}
}
};
try {
AspectJPlugin.getWorkspace().run(r, null);
} catch (CoreException cEx) {
AJDTErrorHandler.handleAJDTError(
UIMessages.CompilerTaskListManager_Error_adding_problem_markers, cEx);
}
// Part of the fix for bug 89793 - editor image is not updated
Collection<AspectJEditor> activeEditorList = AspectJEditor.getActiveEditorList();
synchronized(activeEditorList) {
for(AspectJEditor editor : activeEditorList) {
editor.resetTitleImage();
}
}
}
private void clearMessages() {
affectedResources.clear();
problems.clear();
}
/**
* Try to map a source location in a project to an IResource
*
* @param sloc
* the source location
* @param project
* the project to look in first
* @return the IResource if a match was found, null otherwise
*/
private IResource locationToResource(ISourceLocation sloc, IProject project) {
IResource resource = null;
File file = sloc.getSourceFile();
String loc = file.getPath();
if (!file.exists()) {
// 167121: might be a binary file in a directory, which uses ! as a separator
// - see org.aspectj.weaver.ShadowMunger.getBinaryFile()
loc = loc.replace('!', File.separatorChar);
}
// try this project
FileURICache fileCache = ((CoreCompilerConfiguration) AspectJPlugin.getDefault().getCompilerFactory().getCompilerForProject(project).getCompilerConfiguration()).getFileCache();
resource = fileCache.findResource(loc, project);
if (resource == null) {
// try any project
resource = fileCache.findResource(loc);
if (resource == null) {
// fix for declare
// warning/error bug which
// returns only file name
// (unqualified)
resource = tryToFindResource(loc,project);
}
// At least warn that you are going to
// blow up with an event trace ...
if (resource == null) {
AJLog.log(AJLog.COMPILER,"Whilst adding post compilation markers to resources, cannot locate valid eclipse resource for file " //$NON-NLS-1$
+ loc);
}
}
return resource;
}
private IResource tryToFindResource(String fileName, IProject project) {
IResource ret = null;
String toFind = fileName.replace('\\', '/');
IJavaProject jProject = JavaCore.create(project);
try {
IClasspathEntry[] classpathEntries = jProject
.getResolvedClasspath(false);
for (int i = 0; i < classpathEntries.length; i++) {
IClasspathEntry cpEntry = classpathEntries[i];
if (cpEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
IPath sourcePath = cpEntry.getPath();
// remove the first segment because the findMember call
// following always adds it back in under the covers (doh!)
// and we end up with two first segments otherwise!
sourcePath = sourcePath.removeFirstSegments(1);
IResource memberResource = project.findMember(sourcePath);
if (memberResource != null) {
IResource[] srcContainer = new IResource[] { memberResource };
ret = findFile(srcContainer, toFind);
}
} else if (cpEntry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
IPath projPath = cpEntry.getPath();
IResource projResource = AspectJPlugin.getWorkspace().getRoot().findMember(projPath);
if (projResource != null) {
ret = findFile(new IResource[] { projResource }, toFind);
}
}
if (ret != null) {
break;
}
}
} catch (JavaModelException jmEx) {
AJDTErrorHandler.handleAJDTError(UIMessages.jmCoreException, jmEx);
}
if (ret == null)
ret = project;
return ret;
}
private IResource findFile(IResource[] srcContainer, String name) {
IResource ret = null;
try {
for (int i = 0; i < srcContainer.length; i++) {
IResource ir = srcContainer[i];
if (ir != null) {
if (ir.getFullPath().toString().endsWith(name)) {
ret = ir;
break;
}
if (ir instanceof IContainer) {
ret = findFile(((IContainer) ir).members(), name);
if (ret != null)
break;
}
}
}
} catch (Exception e) {
}
return ret;
}
/**
* returns -1 if problem is not a task and the tasks priority otherwise
* takes case sensitivity into account though this does not seem to
* supported by the current compiler (AJDT 1.1.10)
*/
private int getTaskPriority(ProblemTracker p) {
if (p == null)
return -1;
String message = p.message;
Preferences pref = JavaCore.getPlugin().getPluginPreferences();
String tags = pref.getString("org.eclipse.jdt.core.compiler.taskTags"); //$NON-NLS-1$
String caseSens = pref
.getString("org.eclipse.jdt.core.compiler.taskCaseSensitive"); //$NON-NLS-1$
String priorities = pref
.getString("org.eclipse.jdt.core.compiler.taskPriorities"); //$NON-NLS-1$
boolean caseSensitive;
if (caseSens.equals("disabled")) { //$NON-NLS-1$
caseSensitive = false;
} else {
caseSensitive = true;
}
StringTokenizer tagTokens = new StringTokenizer(tags, ","); //$NON-NLS-1$
StringTokenizer priorityTokens = new StringTokenizer(priorities, ","); //$NON-NLS-1$
while (tagTokens.hasMoreTokens()) {
String prio = priorityTokens.nextToken();
String token = tagTokens.nextToken();
if (caseSensitive) {
if (message.startsWith(token))
return getPrioritiyFlag(prio);
} else {
if (token.length() <= message.length()) {
String temp = message.substring(0, token.length());
if (token.compareToIgnoreCase(temp) == 0)
return getPrioritiyFlag(prio);
}
}
}
return -1;
}
private int getPrioritiyFlag(String prio) {
if (prio.equals("NORMAL")) //$NON-NLS-1$
return IMarker.PRIORITY_NORMAL;
if (prio.equals("HIGH")) //$NON-NLS-1$
return IMarker.PRIORITY_HIGH;
return IMarker.PRIORITY_LOW;
}
private void addOtherProjectMarker(IProject p, IMarker m) {
if (!otherProjectMarkers.containsKey(p.getName())) {
otherProjectMarkers.put(p.getName(), new ArrayList<IMarker>());
}
List<IMarker> l = otherProjectMarkers.get(p.getName());
l.add(m);
}
/**
* Sets the given marker to have hte appropriate severity, according to the
* kind.
*
* @param marker
* the marker to set the message for
* @param kind
* used to determine the appropriate severity
* @throws CoreException
*/
private void setSeverity(IMarker marker, IMessage.Kind kind)
throws CoreException {
if (kind == IMessage.ERROR) {
marker.setAttribute(IMarker.SEVERITY, new Integer(
IMarker.SEVERITY_ERROR));
} else if (kind == IMessage.WARNING) {
marker.setAttribute(IMarker.SEVERITY, new Integer(
IMarker.SEVERITY_WARNING));
} else {
marker.setAttribute(IMarker.SEVERITY, new Integer(
IMarker.SEVERITY_INFO));
}
}
private final static int MAX_MESSAGE_LENGTH = (int) Math.pow(2, 16);
/**
* Sets the given marker to have the appropriate message.
*
* @param marker
* the marker to set the message for
* @param message
* the raw message which may require manipulation
* @param id
* the number of this message, which may be an element of a
* multipart message
* @param count
* the number of parts to this message (most messages are single
* part)
* @throws CoreException
*/
private void setMessage(IMarker marker, String message)
throws CoreException {
if (message == null) {
return;
}
// FIXME: Remove this horrid hack.
// Hack the filename off the front and the line number
// off the end
if (message.indexOf("\":") != -1 && message.indexOf(", at line") != -1) { //$NON-NLS-1$ //$NON-NLS-2$
String hackedMessage = message
.substring(message.indexOf("\":") + 2); //$NON-NLS-1$
message = hackedMessage.substring(0, hackedMessage
.indexOf(", at line")); //$NON-NLS-1$
}
// bug 318150
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=318150
// Can't have more than 2^16 chars in a message
if (message.length() >= MAX_MESSAGE_LENGTH) {
message = message.substring(0, MAX_MESSAGE_LENGTH-1);
}
marker.setAttribute(IMarker.MESSAGE, message);
}
// -------------- other AJDT things -------------------
protected void setLastBuildType(boolean wasFullBuild) {
lastBuildWasFull = wasFullBuild;
}
/**
* clear problems made from a previous compilation stage, but
* keep any project markers.
*/
void clearProblems() {
for (Iterator<ProblemTracker> probIter = problems.iterator(); probIter.hasNext();) {
ProblemTracker problem = (ProblemTracker) probIter.next();
if (problem.location != null) {
probIter.remove();
}
}
}
public static void clearOtherProjectMarkers(IProject p) {
List<?> l = (List<?>) otherProjectMarkers.get(p.getName());
if (l != null) {
ListIterator<?> li = l.listIterator();
while (li.hasNext()) {
IMarker m = (IMarker) li.next();
try {
m.delete();
} catch (CoreException ce) {
// can be ignored
} // not the end of the world.
}
l.clear();
}
}
}