org.eclipse.ajdt.internal.ui.ajde.UIMessageHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ajdt.internal.ui.ajde.UIMessageHandler.java

Source

/********************************************************************
 * 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=" //$NON-NLS-1$
                        + message.getSourceLocation().getLine());
            } 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 //$NON-NLS-1$
                                                        + " Problem message=" //$NON-NLS-1$
                                                        + 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();
        }
    }
}