com.android.ide.eclipse.adt.internal.lint.LintViewPart.java Source code

Java tutorial

Introduction

Here is the source code for com.android.ide.eclipse.adt.internal.lint.LintViewPart.java

Source

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.eclipse.org/org/documents/epl-v10.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.ide.eclipse.adt.internal.lint;

import static com.android.SdkConstants.DOT_JAVA;
import static com.android.SdkConstants.DOT_XML;

import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.preferences.LintPreferencePage;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.tools.lint.detector.api.LintUtils;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.preference.IPreferenceNode;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.preference.PreferenceManager;
import org.eclipse.jface.preference.PreferenceNode;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.TextFileDocumentProvider;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.texteditor.IDocumentProvider;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Eclipse View which shows lint warnings for the current project
 */
public class LintViewPart extends ViewPart implements SelectionListener, IJobChangeListener {
    /** The view id for this view part */
    public static final String ID = "com.android.ide.eclipse.adt.internal.lint.LintViewPart"; //$NON-NLS-1$
    private static final String QUICKFIX_DISABLED_ICON = "quickfix-disabled"; //$NON-NLS-1$
    private static final String QUICKFIX_ICON = "quickfix"; //$NON-NLS-1$
    private static final String REFRESH_ICON = "refresh"; //$NON-NLS-1$
    private static final String EXPAND_DISABLED_ICON = "expandall-disabled"; //$NON-NLS-1$
    private static final String EXPAND_ICON = "expandall"; //$NON-NLS-1$
    private static final String COLUMNS_ICON = "columns"; //$NON-NLS-1$
    private static final String OPTIONS_ICON = "options"; //$NON-NLS-1$
    private static final String IGNORE_THIS_ICON = "ignore-this"; //$NON-NLS-1$
    private static final String IGNORE_THIS_DISABLED_ICON = "ignore-this-disabled"; //$NON-NLS-1$
    private static final String IGNORE_FILE_ICON = "ignore-file"; //$NON-NLS-1$
    private static final String IGNORE_FILE_DISABLED_ICON = "ignore-file-disabled"; //$NON-NLS-1$
    private static final String IGNORE_PRJ_ICON = "ignore-project"; //$NON-NLS-1$
    private static final String IGNORE_PRJ_DISABLED_ICON = "ignore-project-disabled"; //$NON-NLS-1$
    private static final String IGNORE_ALL_ICON = "ignore-all"; //$NON-NLS-1$
    private static final String IGNORE_ALL_DISABLED_ICON = "ignore-all-disabled"; //$NON-NLS-1$
    private IMemento mMemento;
    private LintList mLintView;
    private Text mDetailsText;
    private Label mErrorLabel;
    private SashForm mSashForm;
    private Action mFixAction;
    private Action mRemoveAction;
    private Action mIgnoreAction;
    private Action mAlwaysIgnoreAction;
    private Action mIgnoreFileAction;
    private Action mIgnoreProjectAction;
    private Action mRemoveAllAction;
    private Action mRefreshAction;
    private Action mExpandAll;
    private Action mCollapseAll;
    private Action mConfigureColumns;
    private Action mOptions;

    /**
     * Initial projects to show: this field is only briefly not null during the
     * construction initiated by {@link #show(List)}
     */
    private static List<? extends IResource> sInitialResources;

    /**
     * Constructs a new {@link LintViewPart}
     */
    public LintViewPart() {
    }

    @Override
    public void init(IViewSite site, IMemento memento) throws PartInitException {
        super.init(site, memento);
        mMemento = memento;
    }

    @Override
    public void saveState(IMemento memento) {
        super.saveState(memento);

        mLintView.saveState(memento);
    }

    @Override
    public void dispose() {
        if (mLintView != null) {
            mLintView.dispose();
            mLintView = null;
        }
        super.dispose();
    }

    @Override
    public void createPartControl(Composite parent) {
        GridLayout gridLayout = new GridLayout(1, false);
        gridLayout.verticalSpacing = 0;
        gridLayout.marginWidth = 0;
        gridLayout.marginHeight = 0;
        parent.setLayout(gridLayout);

        mErrorLabel = new Label(parent, SWT.NONE);
        mErrorLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));

        mSashForm = new SashForm(parent, SWT.NONE);
        mSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
        mLintView = new LintList(getSite(), mSashForm, mMemento, false /*singleFile*/);

        mDetailsText = new Text(mSashForm, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL | SWT.MULTI);
        Display display = parent.getDisplay();
        mDetailsText.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
        mDetailsText.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));

        mLintView.addSelectionListener(this);
        mSashForm.setWeights(new int[] { 8, 2 });

        createActions();
        initializeToolBar();

        // If there are currently running jobs, listen for them such that we can update the
        // button state
        refreshStopIcon();

        if (sInitialResources != null) {
            mLintView.setResources(sInitialResources);
            sInitialResources = null;
        } else {
            // No supplied context: show lint warnings for all projects
            IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects(null);
            if (androidProjects.length > 0) {
                List<IResource> projects = new ArrayList<IResource>();
                for (IJavaProject project : androidProjects) {
                    projects.add(project.getProject());
                }
                mLintView.setResources(projects);
            }
        }

        updateIssueCount();
    }

    /**
     * Create the actions.
     */
    private void createActions() {
        ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
        IconFactory iconFactory = IconFactory.getInstance();
        mFixAction = new LintViewAction("Fix", ACTION_FIX, iconFactory.getImageDescriptor(QUICKFIX_ICON),
                iconFactory.getImageDescriptor(QUICKFIX_DISABLED_ICON));

        mIgnoreAction = new LintViewAction("Suppress this error with an annotation/attribute", ACTION_IGNORE_THIS,
                iconFactory.getImageDescriptor(IGNORE_THIS_ICON),
                iconFactory.getImageDescriptor(IGNORE_THIS_DISABLED_ICON));
        mIgnoreFileAction = new LintViewAction("Ignore in this file", ACTION_IGNORE_FILE,
                iconFactory.getImageDescriptor(IGNORE_FILE_ICON),
                iconFactory.getImageDescriptor(IGNORE_FILE_DISABLED_ICON));
        mIgnoreProjectAction = new LintViewAction("Ignore in this project", ACTION_IGNORE_TYPE,
                iconFactory.getImageDescriptor(IGNORE_PRJ_ICON),
                iconFactory.getImageDescriptor(IGNORE_PRJ_DISABLED_ICON));
        mAlwaysIgnoreAction = new LintViewAction("Always Ignore", ACTION_IGNORE_ALL,
                iconFactory.getImageDescriptor(IGNORE_ALL_ICON),
                iconFactory.getImageDescriptor(IGNORE_ALL_DISABLED_ICON));

        mRemoveAction = new LintViewAction("Remove", ACTION_REMOVE,
                sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVE),
                sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVE_DISABLED));
        mRemoveAllAction = new LintViewAction("Remove All", ACTION_REMOVE_ALL,
                sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVEALL),
                sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVEALL_DISABLED));
        mRefreshAction = new LintViewAction("Refresh (& Save Files)", ACTION_REFRESH,
                iconFactory.getImageDescriptor(REFRESH_ICON), null);
        mRemoveAllAction.setEnabled(true);
        mCollapseAll = new LintViewAction("Collapse All", ACTION_COLLAPSE,
                sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_COLLAPSEALL),
                sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_COLLAPSEALL_DISABLED));
        mCollapseAll.setEnabled(true);
        mExpandAll = new LintViewAction("Expand All", ACTION_EXPAND, iconFactory.getImageDescriptor(EXPAND_ICON),
                iconFactory.getImageDescriptor(EXPAND_DISABLED_ICON));
        mExpandAll.setEnabled(true);

        mConfigureColumns = new LintViewAction("Configure Columns...", ACTION_COLUMNS,
                iconFactory.getImageDescriptor(COLUMNS_ICON), null);

        mOptions = new LintViewAction("Options...", ACTION_OPTIONS, iconFactory.getImageDescriptor(OPTIONS_ICON),
                null);

        enableActions(Collections.<IMarker>emptyList(), false /*updateWidgets*/);
    }

    /**
     * Initialize the toolbar.
     */
    private void initializeToolBar() {
        IToolBarManager toolbarManager = getViewSite().getActionBars().getToolBarManager();
        toolbarManager.add(mRefreshAction);
        toolbarManager.add(mFixAction);
        toolbarManager.add(mIgnoreAction);
        toolbarManager.add(mIgnoreFileAction);
        toolbarManager.add(mIgnoreProjectAction);
        toolbarManager.add(mAlwaysIgnoreAction);
        toolbarManager.add(new Separator());
        toolbarManager.add(mRemoveAction);
        toolbarManager.add(mRemoveAllAction);
        toolbarManager.add(new Separator());
        toolbarManager.add(mExpandAll);
        toolbarManager.add(mCollapseAll);
        toolbarManager.add(mConfigureColumns);
        toolbarManager.add(mOptions);
    }

    @Override
    public void setFocus() {
        mLintView.setFocus();
    }

    /**
     * Sets the resource associated with the lint view
     *
     * @param resources the associated resources
     */
    public void setResources(List<? extends IResource> resources) {
        mLintView.setResources(resources);

        // Refresh the stop/refresh icon status
        refreshStopIcon();
    }

    private void refreshStopIcon() {
        Job[] currentJobs = LintJob.getCurrentJobs();
        if (currentJobs.length > 0) {
            ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
            mRefreshAction.setImageDescriptor(sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_STOP));
            for (Job job : currentJobs) {
                job.addJobChangeListener(this);
            }
        } else {
            mRefreshAction.setImageDescriptor(IconFactory.getInstance().getImageDescriptor(REFRESH_ICON));

        }
    }

    // ---- Implements SelectionListener ----

    @Override
    public void widgetSelected(SelectionEvent e) {
        List<IMarker> markers = mLintView.getSelectedMarkers();
        if (markers.size() != 1) {
            mDetailsText.setText(""); //$NON-NLS-1$
        } else {
            mDetailsText.setText(EclipseLintClient.describe(markers.get(0)));
        }

        IStatusLineManager status = getViewSite().getActionBars().getStatusLineManager();
        status.setMessage(mDetailsText.getText());

        updateIssueCount();

        enableActions(markers, true /* updateWidgets */);
    }

    private void enableActions(List<IMarker> markers, boolean updateWidgets) {
        // Update enabled state of actions
        boolean hasSelection = markers.size() > 0;
        boolean canFix = hasSelection;
        for (IMarker marker : markers) {
            if (!LintFix.hasFix(EclipseLintClient.getId(marker))) {
                canFix = false;
                break;
            }

            // Some fixes cannot be run in bulk
            if (markers.size() > 1) {
                List<LintFix> fixes = LintFix.getFixes(EclipseLintClient.getId(marker), marker);
                if (fixes == null || !fixes.get(0).isBulkCapable()) {
                    canFix = false;
                    break;
                }
            }
        }

        boolean haveFile = false;
        boolean isJavaOrXml = true;
        for (IMarker marker : markers) {
            IResource resource = marker.getResource();
            if (resource instanceof IFile || resource instanceof IFolder) {
                haveFile = true;
                String name = resource.getName();
                if (!LintUtils.endsWith(name, DOT_XML) && !LintUtils.endsWith(name, DOT_JAVA)) {
                    isJavaOrXml = false;
                }
                break;
            }
        }

        mFixAction.setEnabled(canFix);
        mIgnoreAction.setEnabled(hasSelection && haveFile && isJavaOrXml);
        mIgnoreFileAction.setEnabled(hasSelection && haveFile);
        mIgnoreProjectAction.setEnabled(hasSelection);
        mAlwaysIgnoreAction.setEnabled(hasSelection);
        mRemoveAction.setEnabled(hasSelection);

        if (updateWidgets) {
            getViewSite().getActionBars().getToolBarManager().update(false);
        }
    }

    @Override
    public void widgetDefaultSelected(SelectionEvent e) {
        Object source = e.getSource();
        if (source == mLintView.getTreeViewer().getControl()) {
            // Jump to editor
            List<IMarker> selection = mLintView.getSelectedMarkers();
            if (selection.size() > 0) {
                EclipseLintClient.showMarker(selection.get(0));
            }
        }
    }

    // --- Implements IJobChangeListener ----

    @Override
    public void done(IJobChangeEvent event) {
        mRefreshAction.setImageDescriptor(IconFactory.getInstance().getImageDescriptor(REFRESH_ICON));

        if (!mLintView.isDisposed()) {
            mLintView.getDisplay().asyncExec(new Runnable() {
                @Override
                public void run() {
                    if (!mLintView.isDisposed()) {
                        updateIssueCount();
                    }
                }
            });
        }
    }

    private void updateIssueCount() {
        int errors = mLintView.getErrorCount();
        int warnings = mLintView.getWarningCount();
        mErrorLabel.setText(String.format("%1$d errors, %2$d warnings", errors, warnings));
    }

    @Override
    public void aboutToRun(IJobChangeEvent event) {
    }

    @Override
    public void awake(IJobChangeEvent event) {
    }

    @Override
    public void running(IJobChangeEvent event) {
    }

    @Override
    public void scheduled(IJobChangeEvent event) {
    }

    @Override
    public void sleeping(IJobChangeEvent event) {
    }

    // ---- Actions ----

    private static final int ACTION_REFRESH = 1;
    private static final int ACTION_FIX = 2;
    private static final int ACTION_IGNORE_THIS = 3;
    private static final int ACTION_IGNORE_FILE = 4;
    private static final int ACTION_IGNORE_TYPE = 5;
    private static final int ACTION_IGNORE_ALL = 6;
    private static final int ACTION_REMOVE = 7;
    private static final int ACTION_REMOVE_ALL = 8;
    private static final int ACTION_COLLAPSE = 9;
    private static final int ACTION_EXPAND = 10;
    private static final int ACTION_COLUMNS = 11;
    private static final int ACTION_OPTIONS = 12;

    private class LintViewAction extends Action {

        private final int mAction;

        private LintViewAction(String label, int action, ImageDescriptor imageDesc,
                ImageDescriptor disabledImageDesc) {
            super(label);
            mAction = action;
            setImageDescriptor(imageDesc);
            if (disabledImageDesc != null) {
                setDisabledImageDescriptor(disabledImageDesc);
            }
        }

        @Override
        public void run() {
            switch (mAction) {
            case ACTION_REFRESH: {
                IWorkbench workbench = PlatformUI.getWorkbench();
                if (workbench != null) {
                    workbench.saveAllEditors(false /*confirm*/);
                }

                Job[] jobs = LintJob.getCurrentJobs();
                if (jobs.length > 0) {
                    EclipseLintRunner.cancelCurrentJobs(false);
                } else {
                    List<? extends IResource> resources = mLintView.getResources();
                    if (resources == null) {
                        return;
                    }
                    Job job = EclipseLintRunner.startLint(resources, null, null, false /*fatalOnly*/,
                            false /*show*/);
                    if (job != null && workbench != null) {
                        job.addJobChangeListener(LintViewPart.this);
                        ISharedImages sharedImages = workbench.getSharedImages();
                        setImageDescriptor(sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_STOP));
                    }
                }
                break;
            }
            case ACTION_FIX: {
                List<IMarker> markers = mLintView.getSelectedMarkers();
                for (IMarker marker : markers) {
                    List<LintFix> fixes = LintFix.getFixes(EclipseLintClient.getId(marker), marker);
                    if (fixes == null) {
                        continue;
                    }
                    LintFix fix = fixes.get(0);
                    IResource resource = marker.getResource();
                    if (fix.needsFocus() && resource instanceof IFile) {
                        IRegion region = null;
                        try {
                            int start = marker.getAttribute(IMarker.CHAR_START, -1);
                            int end = marker.getAttribute(IMarker.CHAR_END, -1);
                            if (start != -1) {
                                region = new Region(start, end - start);
                            }
                            AdtPlugin.openFile((IFile) resource, region);
                        } catch (PartInitException e) {
                            AdtPlugin.log(e, "Can't open file %1$s", resource);
                        }
                    }
                    IDocumentProvider provider = new TextFileDocumentProvider();
                    try {
                        provider.connect(resource);
                        IDocument document = provider.getDocument(resource);
                        if (document != null) {
                            fix.apply(document);
                            if (!fix.needsFocus()) {
                                provider.saveDocument(new NullProgressMonitor(), resource, document,
                                        true /*overwrite*/);
                            }
                        }
                    } catch (Exception e) {
                        AdtPlugin.log(e, "Did not find associated editor to apply fix: %1$s", resource.getName());
                    } finally {
                        provider.disconnect(resource);
                    }
                }
                break;
            }
            case ACTION_REMOVE: {
                for (IMarker marker : mLintView.getSelectedMarkers()) {
                    try {
                        marker.delete();
                    } catch (CoreException e) {
                        AdtPlugin.log(e, null);
                    }
                }
                break;
            }
            case ACTION_REMOVE_ALL: {
                List<? extends IResource> resources = mLintView.getResources();
                if (resources != null) {
                    for (IResource resource : resources) {
                        EclipseLintClient.clearMarkers(resource);
                    }
                }
                break;
            }
            case ACTION_IGNORE_ALL:
                assert false;
                break;
            case ACTION_IGNORE_TYPE:
            case ACTION_IGNORE_FILE: {
                boolean ignoreInFile = mAction == ACTION_IGNORE_FILE;
                for (IMarker marker : mLintView.getSelectedMarkers()) {
                    String id = EclipseLintClient.getId(marker);
                    if (id != null) {
                        IResource resource = marker.getResource();
                        LintFixGenerator.suppressDetector(id, true, ignoreInFile ? resource : resource.getProject(),
                                ignoreInFile);
                    }
                }
                break;
            }
            case ACTION_IGNORE_THIS: {
                for (IMarker marker : mLintView.getSelectedMarkers()) {
                    LintFixGenerator.addSuppressAnnotation(marker);
                }
                break;
            }
            case ACTION_COLLAPSE: {
                mLintView.collapseAll();
                break;
            }
            case ACTION_EXPAND: {
                mLintView.expandAll();
                break;
            }
            case ACTION_COLUMNS: {
                mLintView.configureColumns();
                break;
            }
            case ACTION_OPTIONS: {
                PreferenceManager manager = new PreferenceManager();

                LintPreferencePage page = new LintPreferencePage();
                String title = "Default/Global Settings";
                page.setTitle(title);
                IPreferenceNode node = new PreferenceNode(title, page);
                manager.addToRoot(node);

                List<? extends IResource> resources = mLintView.getResources();
                if (resources != null) {
                    Set<IProject> projects = new HashSet<IProject>();
                    for (IResource resource : resources) {
                        projects.add(resource.getProject());
                    }
                    if (projects.size() > 0) {
                        for (IProject project : projects) {
                            page = new LintPreferencePage();
                            page.setTitle(String.format("Settings for %1$s", project.getName()));
                            page.setElement(project);
                            node = new PreferenceNode(project.getName(), page);
                            manager.addToRoot(node);
                        }
                    }
                }

                Shell shell = LintViewPart.this.getSite().getShell();
                PreferenceDialog dialog = new PreferenceDialog(shell, manager);
                dialog.create();
                dialog.setSelectedNode(title);
                dialog.open();
                break;
            }
            default:
                assert false : mAction;
            }
            updateIssueCount();
        }
    }

    /**
     * Shows or reconfigures the LintView to show the lint warnings for the
     * given project
     *
     * @param projects the projects to show lint warnings for
     */
    public static void show(List<? extends IResource> projects) {
        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
        if (window != null) {
            IWorkbenchPage page = window.getActivePage();
            if (page != null) {
                try {
                    // Pass initial project context via static field read by constructor
                    sInitialResources = projects;
                    IViewPart view = page.showView(LintViewPart.ID, null, IWorkbenchPage.VIEW_ACTIVATE);
                    if (sInitialResources != null && view instanceof LintViewPart) {
                        // The view must be showing already since the constructor was not
                        // run, so reconfigure the view instead
                        LintViewPart lintView = (LintViewPart) view;
                        lintView.setResources(projects);
                    }
                } catch (PartInitException e) {
                    AdtPlugin.log(e, "Cannot open Lint View");
                } finally {
                    sInitialResources = null;
                }
            }
        }
    }
}