| /******************************************************************** |
| * 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(); |
| } |
| } |
| } |