com.android.ide.eclipse.adt.internal.preferences.LintPreferencePage.java Source code

Java tutorial

Introduction

Here is the source code for com.android.ide.eclipse.adt.internal.preferences.LintPreferencePage.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.preferences;

import static com.android.tools.lint.detector.api.TextFormat.RAW;
import static com.android.tools.lint.detector.api.TextFormat.TEXT;

import com.android.annotations.NonNull;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
import com.android.ide.eclipse.adt.internal.lint.EclipseLintClient;
import com.android.ide.eclipse.adt.internal.lint.EclipseLintRunner;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.tools.lint.client.api.Configuration;
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.TextFormat;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.TreeNodeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.dialogs.PropertyPage;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/** Preference page for configuring Lint preferences */
public class LintPreferencePage extends PropertyPage
        implements IWorkbenchPreferencePage, SelectionListener, ControlListener, ModifyListener {
    private static final String ID = "com.android.ide.eclipse.common.preferences.LintPreferencePage"; //$NON-NLS-1$
    private static final int ID_COLUMN_WIDTH = 150;

    private EclipseLintClient mClient;
    private IssueRegistry mRegistry;
    private Configuration mConfiguration;
    private IProject mProject;
    private Map<Issue, Severity> mSeverities = new HashMap<Issue, Severity>();
    private Map<Issue, Severity> mInitialSeverities = Collections.<Issue, Severity>emptyMap();
    private boolean mIgnoreEvent;

    private Tree mTree;
    private TreeViewer mTreeViewer;
    private Text mDetailsText;
    private Button mCheckFileCheckbox;
    private Button mCheckExportCheckbox;
    private Link mWorkspaceLink;
    private TreeColumn mNameColumn;
    private TreeColumn mIdColumn;
    private Combo mSeverityCombo;
    private Button mIncludeAll;
    private Button mIgnoreAll;
    private Text mSearch;

    /**
     * Create the preference page.
     */
    public LintPreferencePage() {
        setPreferenceStore(AdtPlugin.getDefault().getPreferenceStore());
    }

    @Override
    public Control createContents(Composite parent) {
        IAdaptable resource = getElement();
        if (resource != null) {
            mProject = (IProject) resource.getAdapter(IProject.class);
        }

        Composite container = new Composite(parent, SWT.NULL);
        container.setLayout(new GridLayout(2, false));

        if (mProject != null) {
            Label projectLabel = new Label(container, SWT.CHECK);
            projectLabel.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, false, false, 1, 1));
            projectLabel.setText("Project-specific configuration:");

            mWorkspaceLink = new Link(container, SWT.NONE);
            mWorkspaceLink.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
            mWorkspaceLink.setText("<a>Configure Workspace Settings...</a>");
            mWorkspaceLink.addSelectionListener(this);
        } else {
            mCheckFileCheckbox = new Button(container, SWT.CHECK);
            mCheckFileCheckbox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
            mCheckFileCheckbox.setSelection(true);
            mCheckFileCheckbox.setText("When saving files, check for errors");

            mCheckExportCheckbox = new Button(container, SWT.CHECK);
            mCheckExportCheckbox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
            mCheckExportCheckbox.setSelection(true);
            mCheckExportCheckbox
                    .setText("Run full error check when exporting app and abort if fatal errors are found");

            Label separator = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL);
            separator.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1));

            Label checkListLabel = new Label(container, SWT.NONE);
            checkListLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
            checkListLabel.setText("Issues:");
        }

        mRegistry = EclipseLintClient.getRegistry();
        mClient = new EclipseLintClient(mRegistry, mProject != null ? Collections.singletonList(mProject) : null,
                null, false);
        Project project = null;
        if (mProject != null) {
            File dir = AdtUtils.getAbsolutePath(mProject).toFile();
            project = mClient.getProject(dir, dir);
        }
        mConfiguration = mClient.getConfigurationFor(project);

        mSearch = new Text(container, SWT.SEARCH | SWT.ICON_CANCEL | SWT.ICON_SEARCH);
        mSearch.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
        mSearch.addSelectionListener(this);
        mSearch.addModifyListener(this);
        // Grab the Enter key such that pressing return in the search box filters (instead
        // of closing the options dialog)
        mSearch.setMessage("type filter text (use ~ to filter by severity, e.g. ~ignore)");
        mSearch.addTraverseListener(new TraverseListener() {
            @Override
            public void keyTraversed(TraverseEvent e) {
                if (e.keyCode == SWT.CR) {
                    updateFilter();
                    e.doit = false;
                } else if (e.keyCode == SWT.ARROW_DOWN) {
                    // Allow moving from the search into the table
                    if (mTree.getItemCount() > 0) {
                        TreeItem firstCategory = mTree.getItem(0);
                        if (firstCategory.getItemCount() > 0) {
                            TreeItem first = firstCategory.getItem(0);
                            mTree.setFocus();
                            mTree.select(first);
                        }
                    }
                }
            }
        });

        mTreeViewer = new TreeViewer(container, SWT.BORDER | SWT.FULL_SELECTION);
        mTree = mTreeViewer.getTree();
        GridData gdTable = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
        gdTable.widthHint = 500;
        gdTable.heightHint = 160;
        mTree.setLayoutData(gdTable);
        mTree.setLinesVisible(true);
        mTree.setHeaderVisible(true);

        TreeViewerColumn column1 = new TreeViewerColumn(mTreeViewer, SWT.NONE);
        mIdColumn = column1.getColumn();
        mIdColumn.setWidth(100);
        mIdColumn.setText("Id");

        TreeViewerColumn column2 = new TreeViewerColumn(mTreeViewer, SWT.FILL);
        mNameColumn = column2.getColumn();
        mNameColumn.setWidth(100);
        mNameColumn.setText("Name");

        mTreeViewer.setContentProvider(new ContentProvider());
        mTreeViewer.setLabelProvider(new LabelProvider());

        mDetailsText = new Text(container, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL | SWT.MULTI);
        GridData gdText = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 2);
        gdText.heightHint = 80;
        mDetailsText.setLayoutData(gdText);

        Label severityLabel = new Label(container, SWT.NONE);
        severityLabel.setText("Severity:");

        mSeverityCombo = new Combo(container, SWT.READ_ONLY);
        mSeverityCombo.setItems(new String[] { "(Default)", "Fatal", "Error", "Warning", "Information", "Ignore" });
        GridData gdSeverityCombo = new GridData(SWT.FILL, SWT.TOP, false, false, 1, 1);
        gdSeverityCombo.widthHint = 90;
        mSeverityCombo.setLayoutData(gdSeverityCombo);
        mSeverityCombo.setText("");
        mSeverityCombo.addSelectionListener(this);

        List<Issue> issues = mRegistry.getIssues();
        for (Issue issue : issues) {
            Severity severity = mConfiguration.getSeverity(issue);
            mSeverities.put(issue, severity);
        }
        mInitialSeverities = new HashMap<Issue, Severity>(mSeverities);

        mTreeViewer.setInput(mRegistry);

        mTree.addSelectionListener(this);
        // Add a listener to resize the column to the full width of the table
        mTree.addControlListener(this);

        loadSettings(false);

        mTreeViewer.expandAll();

        return container;
    }

    /**
     * Initialize the preference page.
     */
    @Override
    public void init(IWorkbench workbench) {
        // Initialize the preference page
    }

    @Override
    protected void contributeButtons(Composite parent) {
        super.contributeButtons(parent);

        // Add "Include All" button for quickly enabling all the detectors, including
        // those disabled by default
        mIncludeAll = new Button(parent, SWT.PUSH);
        mIncludeAll.setText("Include All");
        mIncludeAll.addSelectionListener(this);

        // Add "Ignore All" button for quickly disabling all the detectors
        mIgnoreAll = new Button(parent, SWT.PUSH);
        mIgnoreAll.setText("Ignore All");
        mIgnoreAll.addSelectionListener(this);

        // As per the contributeButton javadoc: increase parent's column count for each
        // added button
        ((GridLayout) parent.getLayout()).numColumns += 2;
    }

    @Override
    public void dispose() {
        super.dispose();
        cancelPendingSearch();
    }

    @Override
    protected void performDefaults() {
        super.performDefaults();

        mConfiguration.startBulkEditing();

        List<Issue> issues = mRegistry.getIssues();
        for (Issue issue : issues) {
            mConfiguration.setSeverity(issue, null);
        }

        mConfiguration.finishBulkEditing();

        loadSettings(true);
    }

    @Override
    public boolean performOk() {
        storeSettings();
        return true;
    }

    private void loadSettings(boolean refresh) {
        if (mCheckExportCheckbox != null) {
            AdtPrefs prefs = AdtPrefs.getPrefs();
            mCheckFileCheckbox.setSelection(prefs.isLintOnSave());
            mCheckExportCheckbox.setSelection(prefs.isLintOnExport());
        }

        mSeverities.clear();
        List<Issue> issues = mRegistry.getIssues();
        for (Issue issue : issues) {
            Severity severity = mConfiguration.getSeverity(issue);
            mSeverities.put(issue, severity);
        }

        if (refresh) {
            mTreeViewer.refresh();
        }
    }

    private void storeSettings() {
        // Lint on Save, Lint on Export
        if (mCheckExportCheckbox != null) {
            AdtPrefs prefs = AdtPrefs.getPrefs();
            prefs.setLintOnExport(mCheckExportCheckbox.getSelection());
            prefs.setLintOnSave(mCheckFileCheckbox.getSelection());
        }

        if (mConfiguration == null) {
            return;
        }

        mConfiguration.startBulkEditing();
        try {
            // Severities
            for (Map.Entry<Issue, Severity> entry : mSeverities.entrySet()) {
                Issue issue = entry.getKey();
                Severity severity = entry.getValue();
                if (mConfiguration.getSeverity(issue) != severity) {
                    if ((severity == issue.getDefaultSeverity()) && issue.isEnabledByDefault()) {
                        severity = null;
                    }
                    mConfiguration.setSeverity(issue, severity);
                }
            }
        } finally {
            mConfiguration.finishBulkEditing();
        }

        if (!mInitialSeverities.equals(mSeverities)) {
            // Ask user whether we should re-run the rules.
            MessageDialog dialog = new MessageDialog(null, "Lint Settings Have Changed", null,
                    "The list of enabled checks has changed. Would you like to run lint now "
                            + "to update the results?",
                    MessageDialog.QUESTION, new String[] { "Yes", "No" }, 0); // yes is the default
            int result = dialog.open();
            if (result == 0) {
                // Run lint on all the open Android projects
                IWorkspace workspace = ResourcesPlugin.getWorkspace();
                IProject[] projects = workspace.getRoot().getProjects();
                List<IProject> androidProjects = new ArrayList<IProject>(projects.length);
                for (IProject project : projects) {
                    if (project.isOpen() && BaseProjectHelper.isAndroidProject(project)) {
                        androidProjects.add(project);
                    }
                }

                EclipseLintRunner.startLint(androidProjects, null, null, false /*fatalOnly*/, true /*show*/);
            }
        }
    }

    private void updateFilter() {
        cancelPendingSearch();
        if (!mSearch.isDisposed()) {
            // Clear selection before refiltering since otherwise it might be showing
            // items no longer available in the list.
            mTree.setSelection(new TreeItem[0]);
            mDetailsText.setText("");
            try {
                mIgnoreEvent = true;
                mSeverityCombo.setText("");
                mSeverityCombo.setEnabled(false);
            } finally {
                mIgnoreEvent = false;
            }

            mTreeViewer.getContentProvider().inputChanged(mTreeViewer, null, mRegistry);
            mTreeViewer.refresh();
            mTreeViewer.expandAll();
        }
    }

    private void cancelPendingSearch() {
        if (mPendingUpdate != null) {
            Shell shell = getShell();
            if (!shell.isDisposed()) {
                getShell().getDisplay().timerExec(-1, mPendingUpdate);
            }
            mPendingUpdate = null;
        }
    }

    private Runnable mPendingUpdate;

    private void scheduleSearch() {
        if (mPendingUpdate == null) {
            mPendingUpdate = new Runnable() {
                @Override
                public void run() {
                    mPendingUpdate = null;
                    updateFilter();
                }
            };
        }
        getShell().getDisplay().timerExec(250 /*ms*/, mPendingUpdate);
    }

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

    @Override
    public void widgetSelected(SelectionEvent e) {
        if (mIgnoreEvent) {
            return;
        }

        Object source = e.getSource();
        if (source == mTree) {
            TreeItem item = (TreeItem) e.item;
            Object data = item != null ? item.getData() : null;
            if (data instanceof Issue) {
                Issue issue = (Issue) data;
                String summary = issue.getBriefDescription(TextFormat.TEXT);
                String explanation = issue.getExplanation(TextFormat.TEXT);

                StringBuilder sb = new StringBuilder(summary.length() + explanation.length() + 20);
                sb.append(summary);
                sb.append('\n').append('\n');
                sb.append(explanation);
                mDetailsText.setText(sb.toString());
                try {
                    mIgnoreEvent = true;
                    Severity severity = getSeverity(issue);
                    mSeverityCombo.select(severity.ordinal() + 1); // Skip the default option
                    mSeverityCombo.setEnabled(true);
                } finally {
                    mIgnoreEvent = false;
                }
            } else {
                mDetailsText.setText("");
                try {
                    mIgnoreEvent = true;
                    mSeverityCombo.setText("");
                    mSeverityCombo.setEnabled(false);
                } finally {
                    mIgnoreEvent = false;
                }
            }
        } else if (source == mWorkspaceLink) {
            int result = PreferencesUtil.createPreferenceDialogOn(getShell(), ID, new String[] { ID }, null).open();
            if (result == Window.OK) {
                loadSettings(true);
            }
        } else if (source == mSeverityCombo) {
            int index = mSeverityCombo.getSelectionIndex();
            Issue issue = (Issue) mTree.getSelection()[0].getData();
            Severity severity;
            if (index == -1 || index == 0) {
                // "(Default)"
                severity = issue.getDefaultSeverity();
            } else {
                // -1: Skip the "(Default)"
                severity = Severity.values()[index - 1];
            }
            mSeverities.put(issue, severity);
            mTreeViewer.refresh();
        } else if (source == mIncludeAll) {
            List<Issue> issues = mRegistry.getIssues();
            for (Issue issue : issues) {
                // The default severity is never ignore; for disabled-by-default
                // issues the {@link Issue#isEnabledByDefault()} method is false instead
                mSeverities.put(issue, issue.getDefaultSeverity());
            }
            mTreeViewer.refresh();
        } else if (source == mIgnoreAll) {
            List<Issue> issues = mRegistry.getIssues();
            for (Issue issue : issues) {
                mSeverities.put(issue, Severity.IGNORE);
            }
            mTreeViewer.refresh();
        } else if (source == mSearch) {
            updateFilter();
        }
    }

    private Severity getSeverity(Issue issue) {
        Severity severity = mSeverities.get(issue);
        if (severity != null) {
            return severity;
        }

        return mConfiguration.getSeverity(issue);
    }

    @Override
    public void widgetDefaultSelected(SelectionEvent e) {
        Object source = e.getSource();
        if (source == mTree) {
            widgetSelected(e);
        } else if (source == mSearch) {
            if (e.detail == SWT.CANCEL) {
                // Cancel the search
                mSearch.setText("");
            }
            updateFilter();
        }
    }

    // ---- Implements ModifyListener ----
    @Override
    public void modifyText(ModifyEvent e) {
        if (e.getSource() == mSearch) {
            scheduleSearch();
        }
    }

    // ---- Implements ControlListener ----

    @Override
    public void controlMoved(ControlEvent e) {
    }

    @Override
    public void controlResized(ControlEvent e) {
        Rectangle r = mTree.getClientArea();
        int availableWidth = r.width;

        mIdColumn.setWidth(ID_COLUMN_WIDTH);
        availableWidth -= ID_COLUMN_WIDTH;

        // Name absorbs everything else
        mNameColumn.setWidth(availableWidth);
    }

    private boolean filterMatches(@NonNull String filter, @NonNull Issue issue) {
        return (filter.startsWith("~") //$NON-NLS-1$
                && mConfiguration.getSeverity(issue).getDescription().toLowerCase(Locale.US)
                        .startsWith(filter.substring(1)))
                || issue.getCategory().getName().toLowerCase(Locale.US).startsWith(filter)
                || issue.getCategory().getFullName().toLowerCase(Locale.US).startsWith(filter)
                || issue.getId().toLowerCase(Locale.US).contains(filter)
                || issue.getBriefDescription(RAW).toLowerCase(Locale.US).contains(filter);
    }

    private class ContentProvider extends TreeNodeContentProvider {
        private Map<Category, List<Issue>> mCategoryToIssues;
        private Object[] mElements;

        @Override
        public Object[] getElements(Object inputElement) {
            return mElements;
        }

        @Override
        public boolean hasChildren(Object element) {
            return element instanceof Category;
        }

        @Override
        public Object[] getChildren(Object parentElement) {
            assert mCategoryToIssues != null;
            List<Issue> list = mCategoryToIssues.get(parentElement);
            if (list == null) {
                return new Object[0];
            } else {
                return list.toArray();
            }
        }

        @Override
        public Object getParent(Object element) {
            return null;
        }

        @Override
        public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
            mCategoryToIssues = null;

            String filter = mSearch.isDisposed() ? "" : mSearch.getText().trim();
            if (filter.length() == 0) {
                filter = null;
            } else {
                filter = filter.toLowerCase(Locale.US);
            }

            mCategoryToIssues = new HashMap<Category, List<Issue>>();
            List<Issue> issues = mRegistry.getIssues();
            for (Issue issue : issues) {
                if (filter == null || filterMatches(filter, issue)) {
                    List<Issue> list = mCategoryToIssues.get(issue.getCategory());
                    if (list == null) {
                        list = new ArrayList<Issue>();
                        mCategoryToIssues.put(issue.getCategory(), list);
                    }
                    list.add(issue);
                }
            }

            if (filter == null) {
                // Not filtering: show all categories
                mElements = mRegistry.getCategories().toArray();
            } else {
                // Filtering: only include categories that contain matches
                if (mCategoryToIssues == null) {
                    getChildren(null);
                }

                // Preserve the current category order, so instead of
                // just creating a list of the mCategoryToIssues keyset, add them
                // in the order they appear in in the registry
                List<Category> categories = new ArrayList<Category>(mCategoryToIssues.size());
                for (Category category : mRegistry.getCategories()) {
                    if (mCategoryToIssues.containsKey(category)) {
                        categories.add(category);
                    }
                }
                mElements = categories.toArray();
            }
        }
    }

    private class LabelProvider implements ITableLabelProvider, IColorProvider {

        @Override
        public void addListener(ILabelProviderListener listener) {
        }

        @Override
        public void dispose() {
        }

        @Override
        public boolean isLabelProperty(Object element, String property) {
            return true;
        }

        @Override
        public void removeListener(ILabelProviderListener listener) {
        }

        @Override
        public Image getColumnImage(Object element, int columnIndex) {
            if (element instanceof Category) {
                return null;
            }

            if (columnIndex == 1) {
                Issue issue = (Issue) element;
                Severity severity = mSeverities.get(issue);
                if (severity == null) {
                    return null;
                }

                ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
                switch (severity) {
                case FATAL:
                case ERROR:
                    return sharedImages.getImage(ISharedImages.IMG_OBJS_ERROR_TSK);
                case WARNING:
                    return sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK);
                case INFORMATIONAL:
                    return sharedImages.getImage(ISharedImages.IMG_OBJS_INFO_TSK);
                case IGNORE:
                    return sharedImages.getImage(ISharedImages.IMG_ELCL_REMOVE_DISABLED);
                }
            }
            return null;
        }

        @Override
        public String getColumnText(Object element, int columnIndex) {
            if (element instanceof Category) {
                if (columnIndex == 0) {
                    return ((Category) element).getFullName();
                } else {
                    return null;
                }
            }

            Issue issue = (Issue) element;
            switch (columnIndex) {
            case 0:
                return issue.getId();
            case 1:
                return issue.getBriefDescription(TEXT);
            }

            return null;
        }

        // ---- IColorProvider ----

        @Override
        public Color getForeground(Object element) {
            if (element instanceof Category) {
                return mTree.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
            }

            if (element instanceof Issue) {
                Issue issue = (Issue) element;
                Severity severity = mSeverities.get(issue);
                if (severity == Severity.IGNORE) {
                    return mTree.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
                }
            }

            return null;
        }

        @Override
        public Color getBackground(Object element) {
            if (element instanceof Category) {
                return mTree.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
            }
            return null;
        }
    }
}