1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.eclipse.org/org/documents/epl-v10.php
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.ide.eclipse.adt.internal.lint;
18
19import static com.android.SdkConstants.DOT_XML;
20
21import com.android.ide.eclipse.adt.AdtPlugin;
22import com.android.ide.eclipse.adt.AdtUtils;
23import com.android.ide.eclipse.adt.internal.editors.IconFactory;
24import com.android.tools.lint.detector.api.LintUtils;
25
26import org.eclipse.core.resources.IFile;
27import org.eclipse.core.resources.IProject;
28import org.eclipse.core.resources.IResource;
29import org.eclipse.jdt.core.IJavaProject;
30import org.eclipse.jdt.ui.JavaElementLabelProvider;
31import org.eclipse.jface.action.Action;
32import org.eclipse.jface.action.ActionContributionItem;
33import org.eclipse.jface.action.IAction;
34import org.eclipse.jface.action.IMenuCreator;
35import org.eclipse.jface.action.Separator;
36import org.eclipse.jface.dialogs.MessageDialog;
37import org.eclipse.jface.resource.ImageDescriptor;
38import org.eclipse.jface.viewers.ILabelProvider;
39import org.eclipse.jface.viewers.ISelection;
40import org.eclipse.swt.widgets.Control;
41import org.eclipse.swt.widgets.Menu;
42import org.eclipse.ui.IObjectActionDelegate;
43import org.eclipse.ui.ISharedImages;
44import org.eclipse.ui.IWorkbenchPart;
45import org.eclipse.ui.IWorkbenchWindow;
46import org.eclipse.ui.IWorkbenchWindowPulldownDelegate;
47import org.eclipse.ui.PlatformUI;
48import org.eclipse.ui.texteditor.ITextEditor;
49
50import java.util.ArrayList;
51import java.util.List;
52
53/**
54 * Action which runs Lint on the currently projects (and also provides a
55 * pulldown menu in the toolbar for selecting specifically which projects to
56 * check)
57 */
58public class RunLintAction implements IObjectActionDelegate, IMenuCreator,
59        IWorkbenchWindowPulldownDelegate {
60
61    private ISelection mSelection;
62    private Menu mMenu;
63
64    @Override
65    public void selectionChanged(IAction action, ISelection selection) {
66        mSelection = selection;
67    }
68
69    @Override
70    public void run(IAction action) {
71        List<IProject> projects = getProjects(mSelection, true /* warn */);
72
73        if (!projects.isEmpty()) {
74            EclipseLintRunner.startLint(projects, null, null, false /*fatalOnly*/, true /*show*/);
75        }
76    }
77
78    /** Returns the Android project(s) to apply a lint run to. */
79    static List<IProject> getProjects(ISelection selection, boolean warn) {
80        List<IProject> projects = AdtUtils.getSelectedProjects(selection);
81
82        if (projects.isEmpty() && warn) {
83            MessageDialog.openWarning(AdtPlugin.getDisplay().getActiveShell(), "Lint",
84                    "Could not run Lint: Select an Android project first.");
85        }
86
87        return projects;
88    }
89
90    @Override
91    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
92    }
93
94    @Override
95    public void dispose() {
96        if (mMenu != null) {
97            mMenu.dispose();
98        }
99    }
100
101    @Override
102    public void init(IWorkbenchWindow window) {
103    }
104
105    // ---- IMenuCreator ----
106
107    @Override
108    public Menu getMenu(Control parent) {
109        mMenu = new Menu(parent);
110
111        IconFactory iconFactory = IconFactory.getInstance();
112        ImageDescriptor allIcon = iconFactory.getImageDescriptor("lintrun"); //$NON-NLS-1$
113        LintMenuAction allAction = new LintMenuAction("Check All Projects", allIcon, false, null);
114
115        addAction(allAction);
116        addSeparator();
117        IJavaProject[] projects = AdtUtils.getOpenAndroidProjects();
118        ILabelProvider provider = new JavaElementLabelProvider(
119                JavaElementLabelProvider.SHOW_DEFAULT);
120        for (IJavaProject project : projects) {
121            IProject p = project.getProject();
122            ImageDescriptor icon = ImageDescriptor.createFromImage(provider.getImage(p));
123            String label = String.format("Check %1$s", p.getName());
124            LintMenuAction projectAction = new LintMenuAction(label, icon, false, p);
125            addAction(projectAction);
126        }
127
128        ITextEditor textEditor = AdtUtils.getActiveTextEditor();
129        if (textEditor != null) {
130            IFile file = AdtUtils.getActiveFile();
131            // Currently only supported for XML files
132            if (file != null && LintUtils.endsWith(file.getName(), DOT_XML)) {
133                ImageDescriptor icon = ImageDescriptor.createFromImage(provider.getImage(file));
134                IAction fileAction = new LintMenuAction("Check Current File", icon, false, file);
135
136                addSeparator();
137                addAction(fileAction);
138            }
139        }
140
141        ISharedImages images = PlatformUI.getWorkbench().getSharedImages();
142        ImageDescriptor clear = images.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVEALL);
143        LintMenuAction clearAction = new LintMenuAction("Clear Lint Warnings", clear, true, null);
144        addSeparator();
145        addAction(clearAction);
146
147        return mMenu;
148    }
149
150    private void addAction(IAction action) {
151        ActionContributionItem item = new ActionContributionItem(action);
152        item.fill(mMenu, -1);
153    }
154
155    private void addSeparator() {
156        new Separator().fill(mMenu, -1);
157    }
158
159    @Override
160    public Menu getMenu(Menu parent) {
161        return null;
162    }
163
164    /**
165     * Actions in the pulldown context menu: run lint or clear lint markers on
166     * the given resource
167     */
168    private static class LintMenuAction extends Action {
169        private final boolean mClear;
170        private final IResource mResource;
171
172        /**
173         * Creates a new context menu action
174         *
175         * @param text the label
176         * @param descriptor the icon
177         * @param clear if true, clear lint markers otherwise check the resource
178         * @param resource the resource to check or clear markers for, where
179         *            null means all projects
180         */
181        private LintMenuAction(String text, ImageDescriptor descriptor, boolean clear,
182                IResource resource) {
183            super(text, descriptor);
184            mClear = clear;
185            mResource = resource;
186        }
187
188        @Override
189        public void run() {
190            List<IResource> resources = new ArrayList<IResource>();
191            if (mResource == null) {
192                // All projects
193                IJavaProject[] open = AdtUtils.getOpenAndroidProjects();
194                for (IJavaProject project : open) {
195                    resources.add(project.getProject());
196                }
197            } else {
198                resources.add(mResource);
199            }
200            EclipseLintRunner.cancelCurrentJobs(false);
201            if (mClear) {
202                EclipseLintClient.clearMarkers(resources);
203            } else {
204                EclipseLintRunner.startLint(resources, null, null, false /*fatalOnly*/,
205                        true /*show*/);
206            }
207        }
208    }
209}
210