Package de.tobject.findbugs

Source Code of de.tobject.findbugs.FindbugsPlugin

/*
* FindBugs Eclipse Plug-in.
* Copyright (C) 2003 - 2004, Peter Friese
* Copyright (C) 2004-2005, University of Maryland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

package de.tobject.findbugs;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.annotation.CheckForNull;

import org.dom4j.DocumentException;
import org.eclipse.core.internal.preferences.EclipsePreferences;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.preferences.ScopedPreferenceStore;
import org.osgi.framework.BundleContext;

import de.tobject.findbugs.builder.FindBugsBuilder;
import de.tobject.findbugs.builder.FindBugsWorker;
import de.tobject.findbugs.io.FileOutput;
import de.tobject.findbugs.io.IO;
import de.tobject.findbugs.marker.FindBugsMarker;
import de.tobject.findbugs.nature.FindBugsNature;
import de.tobject.findbugs.preferences.FindBugsConstants;
import de.tobject.findbugs.preferences.FindBugsPreferenceInitializer;
import de.tobject.findbugs.properties.DetectorProvider;
import de.tobject.findbugs.properties.DetectorValidator;
import de.tobject.findbugs.properties.DetectorValidator.ValidationStatus;
import de.tobject.findbugs.reporter.Reporter;
import de.tobject.findbugs.view.IMarkerSelectionHandler;
import de.tobject.findbugs.view.explorer.BugContentProvider;
import edu.umd.cs.findbugs.BugCode;
import edu.umd.cs.findbugs.BugPattern;
import edu.umd.cs.findbugs.DetectorFactoryCollection;
import edu.umd.cs.findbugs.Plugin;
import edu.umd.cs.findbugs.PluginException;
import edu.umd.cs.findbugs.Project;
import edu.umd.cs.findbugs.SortedBugCollection;
import edu.umd.cs.findbugs.Version;
import edu.umd.cs.findbugs.config.UserPreferences;
import edu.umd.cs.findbugs.plugin.eclipse.quickfix.BugResolutionAssociations;
import edu.umd.cs.findbugs.plugin.eclipse.quickfix.BugResolutionLoader;
import edu.umd.cs.findbugs.plugins.DuplicatePluginIdException;


/**
* The main plugin class to be used in the desktop.
*/
public class FindbugsPlugin extends AbstractUIPlugin {
    /**
     * The plug-in identifier of the FindBugs Plug-in (value
     * "edu.umd.cs.findbugs.plugin.eclipse", was
     * <code>"de.tobject.findbugs"</code>).
     */
    public static final String PLUGIN_ID = "edu.umd.cs.findbugs.plugin.eclipse"; //$NON-NLS-1$

    private static final String DEFAULT_CLOUD_ID = "edu.umd.cs.findbugs.cloud.doNothingCloud";

    public static final String ICON_PATH = "icons/";

    @SuppressWarnings("restriction")
    private static final IPath WORKSPACE_PREFS_PATH = Platform.getStateLocation(Platform.getBundle(Platform.PI_RUNTIME))
            .append(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME)
            .append(PLUGIN_ID + "." + EclipsePreferences.PREFS_FILE_EXTENSION);

    @java.lang.SuppressWarnings("restriction")
    public static final IPath DEFAULT_PREFS_PATH = new Path(EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME)
    .append("edu.umd.cs.findbugs.core.prefs");

    public static final IPath DEPRECATED_PREFS_PATH = new Path(".fbprefs");

    public static final String DETAILS_VIEW_ID = "de.tobject.findbugs.view.buginfoview";

    public static final String USER_ANNOTATIONS_VIEW_ID = "de.tobject.findbugs.view.userannotationsview";

    public static final String TREE_VIEW_ID = "de.tobject.findbugs.view.bugtreeview";

    public static final String BUG_CONTENT_PROVIDER_ID = "de.tobject.findbugs.view.explorer.BugContentProvider";

    /** Map containing preloaded ImageDescriptors */
    private final Map<String, ImageDescriptor> imageDescriptors = new HashMap<String, ImageDescriptor>(13);

    /** Controls debugging of the plugin */
    public static boolean DEBUG;


    /**
     * The identifier for the FindBugs builder (value
     * <code>"edu.umd.cs.findbugs.plugin.eclipse.findbugsbuilder"</code>).
     */
    public static final String BUILDER_ID = PLUGIN_ID + ".findbugsBuilder"; //$NON-NLS-1$

    /**
     * The identifier for the FindBugs nature (value
     * <code>"edu.umd.cs.findbugs.plugin.eclipse.findbugsnature"</code>).
     *
     * @see org.eclipse.core.resources.IProject#hasNature(java.lang.String)
     */
    public static final String NATURE_ID = PLUGIN_ID + ".findbugsNature"; //$NON-NLS-1$

    // Debugging options
    private static final String PLUGIN_DEBUG = PLUGIN_ID + "/debug"; //$NON-NLS-1$

    private static final String BUILDER_DEBUG = PLUGIN_ID + "/debug/builder"; //$NON-NLS-1$

    private static final String NATURE_DEBUG = PLUGIN_ID + "/debug/nature"; //$NON-NLS-1$

    private static final String REPORTER_DEBUG = PLUGIN_ID + "/debug/reporter"; //$NON-NLS-1$

    private static final String CONTENT_DEBUG = PLUGIN_ID + "/debug/content"; //$NON-NLS-1$

    private static final String PROFILER_DEBUG = PLUGIN_ID + "/debug/profiler"; //$NON-NLS-1$

    // Persistent and session property keys
    public static final QualifiedName SESSION_PROPERTY_BUG_COLLECTION = new QualifiedName(FindbugsPlugin.PLUGIN_ID
            + ".sessionprops", "bugcollection");

    public static final QualifiedName SESSION_PROPERTY_BUG_COLLECTION_DIRTY = new QualifiedName(FindbugsPlugin.PLUGIN_ID
            + ".sessionprops", "bugcollection.dirty");

    public static final QualifiedName SESSION_PROPERTY_USERPREFS = new QualifiedName(FindbugsPlugin.PLUGIN_ID + ".sessionprops",
            "userprefs");

    public static final QualifiedName SESSION_PROPERTY_SETTINGS_ON = new QualifiedName(
            FindbugsPlugin.PLUGIN_ID + ".sessionprops", "settingsOn");

    public static final String LIST_DELIMITER = ";"; //$NON-NLS-1$

    /** The shared instance. */
    private static FindbugsPlugin plugin;

    private static boolean customDetectorsInitialized;

    /** Resource bundle. */
    private ResourceBundle resourceBundle;

    private BugResolutionAssociations bugResolutions;

    private boolean bugResolutionsLoaded;

    /**
     * Constructor.
     */
    public FindbugsPlugin() {
        plugin = this;
    }

    @Override
    public void start(BundleContext context) throws Exception {
        super.start(context);

        Version.registerApplication("FindBugs-Eclipse", Version.RELEASE);

        // configure debugging
        configurePluginDebugOptions();

        // initialize resource strings
        try {
            resourceBundle = ResourceBundle.getBundle("de.tobject.findbugs.messages"); //this is correct //$NON-NLS-1$
        } catch (MissingResourceException x) {
            resourceBundle = null;
        }

        if (System.getProperty("findbugs.home") == null) {
            // TODO hardcore workaround for findbugs home property
            // - see de.tobject.findbugs.builder.FindBugsWorker.work() too
            String findBugsHome = getFindBugsEnginePluginLocation();
            if (DEBUG) {
                logInfo("FindBugs home is: " + findBugsHome);
            }
            System.setProperty("findbugs.home", findBugsHome);
        }
        if (System.getProperty("findbugs.cloud.default") == null) {
            // TODO hardcore workaround for findbugs default cloud property
            // - see edu.umd.cs.findbugs.cloud.CloudFactory and messages.xml
            String defCloud = DEFAULT_CLOUD_ID;
            if (DEBUG) {
                logInfo("Using default cloud: " + defCloud);
            }
            System.setProperty("findbugs.cloud.default", defCloud);
        }

        // Register our save participant
        FindbugsSaveParticipant saveParticipant = new FindbugsSaveParticipant();
        ResourcesPlugin.getWorkspace().addSaveParticipant(this, saveParticipant);
    }

    public static void dumpClassLoader(Class<?> c) {
        System.out.printf("Class loaders for %s:%n", c.getName());
        ClassLoader loader = c.getClassLoader();
        while (loader != null) {
            System.out.printf("  %s %s%n", loader.toString(),  loader.getClass().getSimpleName());
            loader = loader.getParent();
        }

    }

    /**
     * The complexity of the code below is partly caused by the fact that we
     * might have multiple ways to install and/or enable custom plugins. There
     * are plugins discovered by FB itself, plugins contributed to Eclipse and
     * plugins added by user manually via properties. Plugins can be disabled
     * via code or properties. The code below is still work in progress, see
     * also {@link DetectorProvider#getPluginElements(UserPreferences)}.
     *
     * @param detectorPaths
     *            list of possible detector plugins
     * @param force
     *            true if we MUST set plugins even if the given list is empty
     */
    public static synchronized void applyCustomDetectors(boolean force) {
        if(customDetectorsInitialized && !force) {
            return;
        }
        customDetectorsInitialized = true;
        DetectorValidator validator = new DetectorValidator();
        final SortedSet<String> detectorPaths = new TreeSet<String>();
        SortedMap<String, String> contributedDetectors = DetectorsExtensionHelper.getContributedDetectors();
        UserPreferences corePreferences = getCorePreferences(null, false);
        detectorPaths.addAll(corePreferences.getCustomPlugins(true));
        if(DEBUG) {
            dumpClassLoader(FindbugsPlugin.class);
            dumpClassLoader(Plugin.class);
            System.out.println("applyCustomDetectors - going to add " + detectorPaths.size() + " plugin urls...");
            for (String url : detectorPaths) {
                System.out.println("\t" + url);
            }
        }

        // disable custom plugins configured via properties, if they are already loaded
        Set<String> disabledPlugins = corePreferences.getCustomPlugins(false);
        Map<URI, Plugin> allPlugins = Plugin.getAllPluginsMap();
        for (Entry<URI, Plugin> entry : allPlugins.entrySet()) {
            Plugin fbPlugin = entry.getValue();
            String pluginId = fbPlugin.getPluginId();
            // ignore all custom plugins with the same plugin id as already loaded
            if(contributedDetectors.containsKey(pluginId)) {
                contributedDetectors.remove(pluginId);
                detectorPaths.remove(pluginId);
            }

            if (fbPlugin.isCorePlugin() || fbPlugin.isInitialPlugin()) {
                continue;
            }
            if (disabledPlugins.contains(entry.getKey().getPath())
                    || disabledPlugins.contains(pluginId)) {
                fbPlugin.setGloballyEnabled(false);
                Plugin.removeCustomPlugin(fbPlugin);
                if (DEBUG) {
                    System.out.println("Removed plugin: " + fbPlugin + " loaded from " + entry.getKey());
                }
            }
        }

        HashSet<Plugin> enabled = new HashSet<Plugin>();

        // adding FindBugs *Eclipse* plugins, key plugin id, value is path
        for (Entry<String, String> entry : contributedDetectors.entrySet()) {
            String pluginId = entry.getKey();
            String pluginPath = entry.getValue();
            URI uri = new File(pluginPath).toURI();
            if (disabledPlugins.contains(pluginId)
                    || disabledPlugins.contains(pluginPath)
                    || allPlugins.containsKey(uri)) {
                continue;
            }
            addCustomPlugin(enabled, uri);
        }

        // adding custom plugins configured via properties, but only if they are not loaded yet
        for (String path : detectorPaths) {
            // this is plugin id, so we can't use it as URL
            if(new Path(path).segmentCount() == 1) {
                continue;
            }
            URI uri = new File(path).toURI();
            if(allPlugins.containsKey(uri)) {
                continue;
            }
            ValidationStatus status = validator.validate(path);
            if (status.isOK()) {
                addCustomPlugin(enabled, uri);
            } else {
                getDefault().getLog().log(status);
            }
        }

        if (DEBUG) {
            System.out.println("applyCustomDetectors - there was " + detectorPaths.size()
                    + " extra FB plugin urls with " + enabled.size()
                    + " valid FB plugins and " + allPlugins.size()
                    + " total plugins registered by FB.");
            for (Entry<URI, Plugin> entry : allPlugins.entrySet()) {
                Plugin fbPlugin = entry.getValue();
                if (fbPlugin.isGloballyEnabled()) {
                    System.out.println("IS  enabled:\t" + fbPlugin.getPluginId());
                } else {
                    System.out.println("NOT enabled:\t" + fbPlugin.getPluginId());
                }
            }
        }
    }

    protected static void addCustomPlugin(HashSet<Plugin> enabled, URI uri) {
        try {
            // bug 3117769 - we must provide our own classloader
            // to allow third-party plugins extend the classpath via
            // "Buddy" classloading
            // see also: Eclipse-BuddyPolicy attribute in MANIFEST.MF
            Plugin fbPlugin = Plugin.addCustomPlugin(uri, FindbugsPlugin.class.getClassLoader());
            if(fbPlugin != null) {
                // TODO line below required to enable this *optional* plugin
                // but it should be taken by FB core from the findbugs.xml,
                // which currently only works for *core* plugins only
                fbPlugin.setGloballyEnabled(true);
                enabled.add(fbPlugin);
            }
        } catch (PluginException e) {
            getDefault().logException(e, "Failed to load plugin for custom detector: " + uri);
        } catch (DuplicatePluginIdException e) {
            getDefault().logException(e, e.getPluginId() + " already loaded from " + e.getPreviouslyLoadedFrom()
                    + ", ignoring: " + uri);
        }
    }

    @Override
    protected void initializeImageRegistry(ImageRegistry reg) {
        for (FindBugsMarker.MarkerRank prio : FindBugsMarker.MarkerRank.values()) {
            ImageDescriptor descriptor = getImageDescriptor(prio.iconName());
            if (descriptor != null) {
                reg.put(prio.iconName(), descriptor);
            }
        }
    }

    /**
     * Returns the shared instance.
     */
    public static FindbugsPlugin getDefault() {
        return plugin;
    }

    /**
     * @return active window instance, never null
     */
    public static IWorkbenchWindow getActiveWorkbenchWindow() {
        if (Display.getCurrent() != null) {
            return getDefault().getWorkbench().getActiveWorkbenchWindow();
        }
        // need to call from UI thread
        final IWorkbenchWindow[] window = new IWorkbenchWindow[1];
        Display.getDefault().syncExec(new Runnable() {
            public void run() {
                window[0] = getDefault().getWorkbench().getActiveWorkbenchWindow();
            }
        });
        return window[0];
    }

    /**
     * Returns the SWT Shell of the active workbench window or <code>null</code>
     * if no workbench window is active.
     *
     * @return the SWT Shell of the active workbench window, or
     *         <code>null</code> if no workbench window is active
     */
    public static Shell getShell() {
        IWorkbenchWindow window = getActiveWorkbenchWindow();
        if (window == null) {
            return null;
        }
        return window.getShell();
    }

    /**
     * Returns the string from the plugin's resource bundle, or 'key' if not
     * found.
     */
    public static String getResourceString(String key) {
        ResourceBundle bundle = FindbugsPlugin.getDefault().getResourceBundle();
        try {
            return bundle.getString(key);
        } catch (MissingResourceException e) {
            return key;
        }
    }

    /**
     * Returns the plugin's resource bundle,
     */
    public ResourceBundle getResourceBundle() {
        return resourceBundle;
    }

    public void configurePluginDebugOptions() {
        if (isDebugging()) {
            // debugging for the plugin itself
            String option = Platform.getDebugOption(PLUGIN_DEBUG);
            FindbugsPlugin.DEBUG = Boolean.valueOf(option).booleanValue();

            // debugging for the builder and friends
            option = Platform.getDebugOption(BUILDER_DEBUG);
            FindBugsBuilder.DEBUG = Boolean.valueOf(option).booleanValue();
            FindBugsWorker.DEBUG = FindBugsBuilder.DEBUG;

            // debugging for the nature
            option = Platform.getDebugOption(NATURE_DEBUG);
            FindBugsNature.DEBUG = Boolean.valueOf(option).booleanValue();

            // debugging for the reporter
            option = Platform.getDebugOption(REPORTER_DEBUG);
            Reporter.DEBUG = Boolean.valueOf(option).booleanValue();

            // debugging for the content provider
            option = Platform.getDebugOption(CONTENT_DEBUG);
            BugContentProvider.DEBUG = Boolean.valueOf(option).booleanValue();

            option = Platform.getDebugOption(PROFILER_DEBUG);
            if (Boolean.valueOf(option).booleanValue()) {
                System.setProperty("profiler.report", "true");
            }
        }
    }

    /**
     * Find the filesystem path of the FindBugs plugin directory.
     *
     * @return the filesystem path of the FindBugs plugin directory, or null if
     *         the FindBugs plugin directory cannot be found
     */
    public static String getFindBugsEnginePluginLocation() {
        // findbugs.home should be set to the directory the plugin is
        // installed in.
        URL u = plugin.getBundle().getEntry("/");
        try {
            URL bundleRoot = FileLocator.resolve(u);
            if (FindBugsBuilder.DEBUG) {
                System.out.println("Pluginpath: " + bundleRoot.getPath()); //$NON-NLS-1$
            }
            return bundleRoot.getPath();
        } catch (IOException e) {
            FindbugsPlugin.getDefault().logException(e, "IO Exception locating engine plugin");
        }
        return null;
    }

    /**
     * @param key
     * @return
     */
    public String getMessage(String key) {
        return getResourceString(key);
    }

    /**
     * Log an exception.
     *
     * @param e
     *            the exception
     * @param message
     *            message describing how/why the exception occurred
     */
    public void logException(Throwable e, String message) {
        logMessage(IStatus.ERROR, message, e);
    }

    /**
     * Log an error.
     *
     * @param message
     *            error message
     */
    public void logError(String message) {
        logMessage(IStatus.ERROR, message, null);
    }

    /**
     * Log a warning.
     *
     * @param message
     *            warning message
     */
    public void logWarning(String message) {
        logMessage(IStatus.WARNING, message, null);
    }

    /**
     * Log an informational message.
     *
     * @param message
     *            the informational message
     */
    public void logInfo(String message) {
        logMessage(IStatus.INFO, message, null);
    }

    public void logMessage(int severity, String message, Throwable e) {
        if (DEBUG) {
            String what = (severity == IStatus.ERROR) ? (e != null ? "Exception" : "Error") : "Warning";
            System.out.println(what + " in FindBugs plugin: " + message);
            if (e != null) {
                e.printStackTrace();
            }
        }
        IStatus status = createStatus(severity, message, e);
        getLog().log(status);
    }

    public static IStatus createStatus(int severity, String message, Throwable e) {
        return new Status(severity, FindbugsPlugin.PLUGIN_ID, 0, message, e);
    }

    public static IStatus createErrorStatus(String message, Throwable e) {
        return new Status(IStatus.ERROR, FindbugsPlugin.PLUGIN_ID, 0, message, e);
    }

    /**
     * Get the file resource used to store findbugs warnings for a project.
     *
     * @param project
     *            the project
     * @return the IPath to the file (which may not actually exist in the
     *         filesystem yet)
     */
    public static IPath getBugCollectionFile(IProject project) {
        // IPath path = project.getWorkingLocation(PLUGIN_ID); //
        // project-specific but not user-specific?
        IPath path = getDefault().getStateLocation(); // user-specific but not
        // project-specific
        return path.append(project.getName() + ".fbwarnings");
    }

    private static boolean isBugCollectionDirty(IProject project) throws CoreException {
        Object dirty = project.getSessionProperty(SESSION_PROPERTY_BUG_COLLECTION_DIRTY);

        if (dirty == null) {
            return false;
        }
        return ((Boolean) dirty).booleanValue();
    }

    public static void markBugCollectionDirty(IProject project, boolean isDirty) throws CoreException {
        project.setSessionProperty(SESSION_PROPERTY_BUG_COLLECTION_DIRTY, isDirty ? Boolean.TRUE : Boolean.FALSE);
    }

    @CheckForNull
    public static SortedBugCollection getBugCollectionIfSet(IProject project) {
        try {
            return (SortedBugCollection) project.getSessionProperty(SESSION_PROPERTY_BUG_COLLECTION);
        } catch (CoreException ignored) {
            FindbugsPlugin.getDefault().logException(ignored, "IO Exception reading project bugs.");
            return null;
        }
    }

    public static SortedBugCollection getBugCollection(IProject project, IProgressMonitor monitor) throws CoreException {
        return getBugCollection(project, monitor, true);
    }

    /**
     * Get the stored BugCollection for project. If there is no stored bug
     * collection for the project, or if an error occurs reading the stored bug
     * collection, a default empty collection is created and returned.
     *
     * @param project
     *            the eclipse project
     * @param monitor
     *            a progress monitor
     * @return the stored BugCollection, never null
     * @throws CoreException
     */
    public static SortedBugCollection getBugCollection(IProject project, IProgressMonitor monitor, boolean useCloud)
            throws CoreException {
        SortedBugCollection bugCollection = (SortedBugCollection) project.getSessionProperty(SESSION_PROPERTY_BUG_COLLECTION);
        if (bugCollection == null) {
            try {
                readBugCollectionAndProject(project, monitor, useCloud);
                bugCollection = (SortedBugCollection) project.getSessionProperty(SESSION_PROPERTY_BUG_COLLECTION);
            } catch (IOException e) {
                FindbugsPlugin.getDefault().logException(e, "Could not read bug collection for project");
                bugCollection = createDefaultEmptyBugCollection(project);
            } catch (DocumentException e) {
                FindbugsPlugin.getDefault().logException(e, "Could not read bug collection for project");
                bugCollection = createDefaultEmptyBugCollection(project);
            }
        }
        return bugCollection;
    }

    private static void cacheBugCollectionAndProject(IProject project, SortedBugCollection bugCollection, Project fbProject)
            throws CoreException {
        project.setSessionProperty(SESSION_PROPERTY_BUG_COLLECTION, bugCollection);
        markBugCollectionDirty(project, false);
    }

    private static SortedBugCollection createDefaultEmptyBugCollection(IProject project) throws CoreException {
        SortedBugCollection bugCollection = new SortedBugCollection();
        Project fbProject = bugCollection.getProject();

        UserPreferences userPrefs = getUserPreferences(project);

        String cloudId = userPrefs.getCloudId();
        if (cloudId != null) {
            fbProject.setCloudId(cloudId);
        }
        cacheBugCollectionAndProject(project, bugCollection, fbProject);
        return bugCollection;
    }

    /**
     * Read saved bug collection and findbugs project from file. Will populate
     * the bug collection and findbugs project session properties if successful.
     * If there is no saved bug collection and project for the eclipse project,
     * then FileNotFoundException will be thrown.
     *
     * @param project
     *            the eclipse project
     * @param monitor
     *            a progress monitor
     * @throws java.io.FileNotFoundException
     *             the saved bug collection doesn't exist
     * @throws IOException
     * @throws DocumentException
     * @throws CoreException
     */
    private static void readBugCollectionAndProject(IProject project, IProgressMonitor monitor, boolean useCloud)
            throws IOException, DocumentException, CoreException {
        SortedBugCollection bugCollection;

        IPath bugCollectionPath = getBugCollectionFile(project);
        // Don't turn the path to an IFile because it isn't local to the
        // project.
        // see the javadoc for org.eclipse.core.runtime.Plugin
        File bugCollectionFile = bugCollectionPath.toFile();
        if (!bugCollectionFile.exists()) {
            // throw new
            // FileNotFoundException(bugCollectionFile.getLocation().toOSString());
            getDefault().logInfo("creating new bug collection: " + bugCollectionPath.toOSString());
            createDefaultEmptyBugCollection(project); // since we no longer
            // throw, have to do this
            // here
            return;
        }

        UserPreferences prefs = getUserPreferences(project);
        bugCollection = new SortedBugCollection();
        bugCollection.getProject().setGuiCallback(new EclipseGuiCallback(project));
        bugCollection.setDoNotUseCloud(!useCloud);

        bugCollection.readXML(bugCollectionFile);
        if (useCloud) {
            String cloudId = prefs.getCloudId();
            if (cloudId != null) {
                bugCollection.getProject().setCloudId(cloudId);
            }
        }

        cacheBugCollectionAndProject(project, bugCollection, bugCollection.getProject());
    }

    /**
     * Store a new bug collection for a project. The collection is stored in the
     * session, and also in a file in the project.
     *
     * @param project
     *            the project
     * @param bugCollection
     *            the bug collection
     * @param monitor
     *            progress monitor
     * @throws IOException
     * @throws CoreException
     */
    public static void storeBugCollection(IProject project, final SortedBugCollection bugCollection, IProgressMonitor monitor)
            throws IOException, CoreException {

        // Store the bug collection and findbugs project in the session
        project.setSessionProperty(SESSION_PROPERTY_BUG_COLLECTION, bugCollection);

        if (bugCollection != null) {
            writeBugCollection(project, bugCollection, monitor);
        }
    }

    /**
     * If necessary, save current bug collection for project to disk.
     *
     * @param project
     *            the project
     * @param monitor
     *            a progress monitor
     * @throws CoreException
     */
    public static void saveCurrentBugCollection(IProject project, IProgressMonitor monitor) throws CoreException {
        if (isBugCollectionDirty(project)) {
            SortedBugCollection bugCollection = (SortedBugCollection) project.getSessionProperty(SESSION_PROPERTY_BUG_COLLECTION);

            if (bugCollection != null) {
                writeBugCollection(project, bugCollection, monitor);
            }
        }
    }

    private static void writeBugCollection(IProject project, final SortedBugCollection bugCollection, IProgressMonitor monitor)
            throws CoreException {
        // Save to file
        IPath bugCollectionPath = getBugCollectionFile(project);
        // Don't turn the path to an IFile because it isn't local to the
        // project.
        // see the javadoc for org.eclipse.core.runtime.Plugin
        File bugCollectionFile = bugCollectionPath.toFile();
        FileOutput fileOutput = new FileOutput() {
            public void writeFile(OutputStream os) throws IOException {
                bugCollection.writeXML(os);
            }

            public String getTaskDescription() {
                return "creating XML FindBugs data file";
            }
        };
        IO.writeFile(bugCollectionFile, fileOutput, monitor);
        markBugCollectionDirty(project, false);
    }

    /**
     * Get the FindBugs preferences file for a project (which may not exist yet)
     *
     * @param project
     *            the project
     * @return the IFile for the FindBugs preferences file, if any. Can be
     *         "empty" handle if the real file does not exist yet
     */
    private static IFile getUserPreferencesFile(IProject project) {
        IFile defaultFile = project.getFile(DEFAULT_PREFS_PATH);
        IFile oldFile = project.getFile(DEPRECATED_PREFS_PATH);
        if (defaultFile.isAccessible() || !oldFile.isAccessible()) {
            return defaultFile;
        }
        return oldFile;
    }

    public static boolean isProjectSettingsEnabled(IProject project) {
        // fast path: read from session, if available
        Boolean enabled;
        try {
            enabled = (Boolean) project.getSessionProperty(SESSION_PROPERTY_SETTINGS_ON);
        } catch (CoreException e) {
            enabled = null;
        }
        if (enabled != null) {
            return enabled.booleanValue();
        }

        // legacy support: before 1.3.8, there was ONLY project preferences in
        // .fbprefs
        // so check if the file is there...
        IFile file = getUserPreferencesFile(project);
        boolean projectPropsEnabled = file.isAccessible();
        if (projectPropsEnabled) {
            ScopedPreferenceStore store = new ScopedPreferenceStore(new ProjectScope(project), PLUGIN_ID);
            // so if the file is there, we can check if after 1.3.8 the flag is
            // set
            // to use workspace properties instead
            projectPropsEnabled = !store.contains(FindBugsConstants.PROJECT_PROPS_DISABLED)
                    || !store.getBoolean(FindBugsConstants.PROJECT_PROPS_DISABLED);
        }
        // remember in the session to speedup access, don't touch the store
        setProjectSettingsEnabled(project, null, projectPropsEnabled);
        return projectPropsEnabled;
    }

    public static void setProjectSettingsEnabled(IProject project, IPreferenceStore store, boolean enabled) {
        try {
            project.setSessionProperty(SESSION_PROPERTY_SETTINGS_ON, Boolean.valueOf(enabled));
        } catch (CoreException e) {
            FindbugsPlugin.getDefault().logException(e, "Error setting FindBugs session property for project");
        }
        if (store != null) {
            store.setValue(FindBugsConstants.PROJECT_PROPS_DISABLED, !enabled);
        }
    }

    /**
     * Get the FindBugs core preferences for given project. This method can
     * return workspace preferences if project preferences are not created yet
     * or they are disabled.
     *
     * @param project
     *            the project (if null, workspace settings are used)
     * @param forceRead
     *            true to enforce reading properties from disk
     *
     * @return the preferences for the project or prefs from workspace
     */
    public static UserPreferences getCorePreferences(IProject project, boolean forceRead) {
        if (project == null || !isProjectSettingsEnabled(project)) {
            // read workspace (user) settings from instance area
            return getWorkspacePreferences();
        }

        // use project settings
        return getProjectPreferences(project, forceRead);
    }

    /**
     * Get the Eclipse plugin preferences for given project. This method can
     * return workspace preferences if project preferences are not created yet
     * or they are disabled.
     *
     * @param project
     *            the project (if null, workspace settings are used)
     *
     * @return the preferences for the project or prefs from workspace
     */
    public static IPreferenceStore getPluginPreferences(IProject project) {
        if (project == null || !isProjectSettingsEnabled(project)) {
            // read workspace (user) settings from instance area
            return new ScopedPreferenceStore(new InstanceScope(), FindbugsPlugin.PLUGIN_ID);
        }

        // use project settings
        return new ScopedPreferenceStore(new ProjectScope(project), FindbugsPlugin.PLUGIN_ID);
    }

    /**
     * Get project own preferences set.
     *
     * @param project
     *            must be non null, exist and be opened
     * @param forceRead
     * @return current project preferences, independently if project preferences
     *         are enabled or disabled for given project.
     */
    public static UserPreferences getProjectPreferences(IProject project, boolean forceRead) {
        try {
            UserPreferences prefs = (UserPreferences) project.getSessionProperty(SESSION_PROPERTY_USERPREFS);
            if (prefs == null || forceRead) {
                prefs = readUserPreferences(project);
                if (prefs == null) {
                    prefs = getWorkspacePreferences().clone();
                }
                project.setSessionProperty(SESSION_PROPERTY_USERPREFS, prefs);
            }
            return prefs;
        } catch (CoreException e) {
            FindbugsPlugin.getDefault().logException(e, "Error getting FindBugs preferences for project");
            return getWorkspacePreferences().clone();
        }
    }

    private static UserPreferences getWorkspacePreferences() {
        // create initially default settings
        UserPreferences userPrefs = FindBugsPreferenceInitializer.createDefaultUserPreferences();
        File prefsFile = WORKSPACE_PREFS_PATH.toFile();
        if (!prefsFile.isFile()) {
            return userPrefs;
        }
        // load custom settings over defaults
        FileInputStream in;
        try {
            in = new FileInputStream(prefsFile);
            userPrefs.read(in);
        } catch (IOException e) {
            FindbugsPlugin.getDefault().logException(e, "Error reading custom FindBugs preferences for workspace");
        }
        return userPrefs;
    }

    /**
     * Get the UserPreferences for given project.
     *
     * @param project
     *            the project
     * @return the UserPreferences for the project
     */
    public static UserPreferences getUserPreferences(IProject project) {
        return getCorePreferences(project, false);
    }

    /**
     * Save current UserPreferences for given project or workspace.
     *
     * @param project
     *            the project or null for workspace
     * @throws CoreException
     */
    public static void saveUserPreferences(IProject project, final UserPreferences userPrefs) throws CoreException {

        FileOutput userPrefsOutput = new FileOutput() {
            public void writeFile(OutputStream os) throws IOException {
                userPrefs.write(os);
            }

            public String getTaskDescription() {
                return "writing user preferences";
            }
        };

        if (project != null) {
            // Make the new user preferences current for the project
            project.setSessionProperty(SESSION_PROPERTY_USERPREFS, userPrefs);
            IFile userPrefsFile = getUserPreferencesFile(project);
            ensureReadWrite(userPrefsFile);
            IO.writeFile(userPrefsFile, userPrefsOutput, null);
            if (project.getFile(DEPRECATED_PREFS_PATH).equals(userPrefsFile)) {
                String message = "Found old style FindBugs preferences for project '" + project.getName()
                        + "'. This preferences are not at the default location: '" + DEFAULT_PREFS_PATH + "'." + " Please move '"
                        + DEPRECATED_PREFS_PATH + "' to '" + DEFAULT_PREFS_PATH + "'.";
                getDefault().logWarning(message);
            }
        } else {
            // write the workspace preferences to the eclipse preference store
            ByteArrayOutputStream bos = new ByteArrayOutputStream(10000);
            try {
                userPrefs.write(bos);
            } catch (IOException e) {
                getDefault().logException(e, "Failed to write user preferences");
                return;
            }
            Properties props = new Properties();
            try {
                props.load(new ByteArrayInputStream(bos.toByteArray()));
            } catch (IOException e) {
                getDefault().logException(e, "Failed to save user preferences");
                return;
            }
            IPreferenceStore store = getDefault().getPreferenceStore();
            for (Entry<Object, Object> entry : props.entrySet()) {
                store.putValue((String) entry.getKey(), (String) entry.getValue());
            }
        }
    }

    /**
     * Ensure that a file is writable. If not currently writable, check it as so
     * that we can edit it.
     *
     * @param file
     *            - file that should be made writable
     * @throws CoreException
     */
    private static void ensureReadWrite(IFile file) throws CoreException {
        /*
         * fix for bug 1683264: we should checkout file before writing to it
         */
        if (file.isReadOnly()) {
            IStatus checkOutStatus = ResourcesPlugin.getWorkspace().validateEdit(new IFile[] { file }, null);
            if (!checkOutStatus.isOK()) {
                throw new CoreException(checkOutStatus);
            }
        }
    }

    /**
     * Read UserPreferences for project from the file in the project directory.
     * Returns null if the preferences have not been saved to a file, or if
     * there is an error reading the preferences file.
     *
     * @param project
     *            the project to get the UserPreferences for
     * @return the UserPreferences, or null if the UserPreferences file could
     *         not be read
     * @throws CoreException
     */
    private static UserPreferences readUserPreferences(IProject project) throws CoreException {
        IFile userPrefsFile = getUserPreferencesFile(project);
        if (!userPrefsFile.exists()) {
            return null;
        }
        try {
            // force is preventing us for out-of-sync exception if file was
            // changed externally
            InputStream in = userPrefsFile.getContents(true);
            UserPreferences userPrefs = FindBugsPreferenceInitializer.createDefaultUserPreferences();
            userPrefs.read(in);
            return userPrefs;
        } catch (IOException e) {
            FindbugsPlugin.getDefault().logException(e, "Could not read user preferences for project");
            return null;
        }
    }

    public BugResolutionAssociations getBugResolutions() {
        if (!bugResolutionsLoaded) {
            bugResolutionsLoaded = true;
            try {
                bugResolutions = loadBugResolutions();
            } catch (Exception e) {
                FindbugsPlugin.getDefault().logException(e, "Could not read load bug resolutions");
            }
        }
        return bugResolutions;
    }

    private BugResolutionAssociations loadBugResolutions() {
        BugResolutionLoader loader = new BugResolutionLoader();
        return loader.loadBugResolutions();
    }

    public static void showMarker(IMarker marker, String viewId, IWorkbenchPart source) {
        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
        IViewPart view = page.findView(viewId);
        if (!page.isPartVisible(view)) {
            try {
                view = page.showView(viewId);
            } catch (PartInitException e) {
                FindbugsPlugin.getDefault().logException(e, "Could not open view: " + viewId);
                return;
            }
        }
        if (view instanceof IMarkerSelectionHandler) {
            IMarkerSelectionHandler handler = (IMarkerSelectionHandler) view;
            handler.markerSelected(source, marker);
        } else if (DETAILS_VIEW_ID.equals(viewId) && view instanceof ISelectionListener) {
            ISelectionListener listener = (ISelectionListener) view;
            listener.selectionChanged(source, new StructuredSelection(marker));
        }
    }

    /**
     * Call this method to retrieve the (cache) ImageDescriptor for the given
     * id.
     *
     * @param id
     *            the id of the image descriptor or relative icon path if icon
     *            is inside of default icons folder
     * @return the ImageDescriptor instance.
     */
    public ImageDescriptor getImageDescriptor(String id) {
        ImageDescriptor imageDescriptor = imageDescriptors.get(id);
        if (imageDescriptor == null) {
            String pluginId = getDefault().getBundle().getSymbolicName();
            imageDescriptor = AbstractUIPlugin.imageDescriptorFromPlugin(pluginId, ICON_PATH + id);
            imageDescriptors.put(id, imageDescriptor);
        }
        return imageDescriptor;
    }

    public static Set<BugPattern> getKnownPatterns() {
        Set<BugPattern> patterns = new TreeSet<BugPattern>();
        Iterator<BugPattern> patternIterator = DetectorFactoryCollection.instance().bugPatternIterator();
        while (patternIterator.hasNext()) {
            patterns.add(patternIterator.next());
        }
        return patterns;
    }

    public static Set<BugCode> getKnownPatternTypes() {
        Set<BugCode> patterns = new TreeSet<BugCode>(DetectorFactoryCollection.instance().getBugCodes());
        return patterns;
    }

    public static Set<String> getFilteredIds() {
        final IPreferenceStore store = FindbugsPlugin.getDefault().getPreferenceStore();
        String lastUsedFilter = store.getString(FindBugsConstants.LAST_USED_EXPORT_FILTER);
        return FindBugsConstants.decodeIds(lastUsedFilter);
    }

    public static Set<BugPattern> getFilteredPatterns() {
        Iterator<BugPattern> patternIterator = DetectorFactoryCollection.instance().bugPatternIterator();
        Set<BugPattern> set = new HashSet<BugPattern>();
        Set<String> patternTypes = getFilteredIds();
        while (patternIterator.hasNext()) {
            BugPattern next = patternIterator.next();
            String patternId = next.getType();
            if (!patternTypes.contains(patternId)) {
                continue;
            }
            set.add(next);
        }
        return set;
    }

    public static Set<BugCode> getFilteredPatternTypes() {
        Set<BugCode> set = new HashSet<BugCode>();
        Set<String> patternTypes = getFilteredIds();
        for(BugCode next :  DetectorFactoryCollection.instance().getBugCodes()) {
            String type = next.getAbbrev();
            if (!patternTypes.contains(type)) {
                continue;
            }
            set.add(next);
        }
        return set;
    }

    public static void clearBugCollection(IProject project) throws CoreException {
        createDefaultEmptyBugCollection(project);
        markBugCollectionDirty(project, true);
        saveCurrentBugCollection(project, null);
    }

}
TOP

Related Classes of de.tobject.findbugs.FindbugsPlugin

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.