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 17 package com.android.ide.eclipse.adt.internal.lint; 18 19 import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML; 20 21 import com.android.ide.eclipse.adt.AdtPlugin; 22 import com.android.ide.eclipse.adt.AdtUtils; 23 import com.android.ide.eclipse.adt.internal.editors.IconFactory; 24 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; 25 import com.android.tools.lint.detector.api.LintUtils; 26 27 import org.eclipse.core.resources.IFile; 28 import org.eclipse.core.resources.IProject; 29 import org.eclipse.core.resources.IResource; 30 import org.eclipse.core.runtime.IAdaptable; 31 import org.eclipse.jdt.core.IJavaProject; 32 import org.eclipse.jdt.ui.JavaElementLabelProvider; 33 import org.eclipse.jface.action.Action; 34 import org.eclipse.jface.action.ActionContributionItem; 35 import org.eclipse.jface.action.IAction; 36 import org.eclipse.jface.action.IMenuCreator; 37 import org.eclipse.jface.action.Separator; 38 import org.eclipse.jface.dialogs.MessageDialog; 39 import org.eclipse.jface.resource.ImageDescriptor; 40 import org.eclipse.jface.viewers.ILabelProvider; 41 import org.eclipse.jface.viewers.ISelection; 42 import org.eclipse.jface.viewers.IStructuredSelection; 43 import org.eclipse.swt.widgets.Control; 44 import org.eclipse.swt.widgets.Menu; 45 import org.eclipse.ui.IObjectActionDelegate; 46 import org.eclipse.ui.ISharedImages; 47 import org.eclipse.ui.IWorkbenchPart; 48 import org.eclipse.ui.IWorkbenchWindow; 49 import org.eclipse.ui.IWorkbenchWindowPulldownDelegate; 50 import org.eclipse.ui.PlatformUI; 51 import org.eclipse.ui.texteditor.ITextEditor; 52 53 import java.util.ArrayList; 54 import java.util.Collections; 55 import java.util.Iterator; 56 import java.util.List; 57 58 /** 59 * Action which runs Lint on the currently projects (and also provides a 60 * pulldown menu in the toolbar for selecting specifically which projects to 61 * check) 62 */ 63 public class RunLintAction implements IObjectActionDelegate, IMenuCreator, 64 IWorkbenchWindowPulldownDelegate { 65 66 private ISelection mSelection; 67 private Menu mMenu; 68 69 @Override selectionChanged(IAction action, ISelection selection)70 public void selectionChanged(IAction action, ISelection selection) { 71 mSelection = selection; 72 } 73 74 @Override run(IAction action)75 public void run(IAction action) { 76 List<IProject> projects = getProjects(mSelection, true /* warn */); 77 78 if (!projects.isEmpty()) { 79 EclipseLintRunner.startLint(projects, null, false /*fatalOnly*/, true /*show*/); 80 } else { 81 MessageDialog.openWarning(AdtPlugin.getDisplay().getActiveShell(), "Lint", 82 "Could not run Lint: Select a project first."); 83 } 84 } 85 86 /** Returns the Android project(s) to apply a lint run to. */ getProjects(ISelection selection, boolean warn)87 static List<IProject> getProjects(ISelection selection, boolean warn) { 88 List<IProject> projects = new ArrayList<IProject>(); 89 90 if (selection instanceof IStructuredSelection) { 91 IStructuredSelection structuredSelection = (IStructuredSelection) selection; 92 // get the unique selected item. 93 Iterator<?> iterator = structuredSelection.iterator(); 94 while (iterator.hasNext()) { 95 Object element = iterator.next(); 96 97 // First look up the resource (since some adaptables 98 // provide an IResource but not an IProject, and we can 99 // always go from IResource to IProject) 100 IResource resource = null; 101 if (element instanceof IResource) { // may include IProject 102 resource = (IResource) element; 103 } else if (element instanceof IAdaptable) { 104 IAdaptable adaptable = (IAdaptable)element; 105 Object adapter = adaptable.getAdapter(IResource.class); 106 resource = (IResource) adapter; 107 } 108 109 // get the project object from it. 110 IProject project = null; 111 if (resource != null) { 112 project = resource.getProject(); 113 } else if (element instanceof IAdaptable) { 114 project = (IProject) ((IAdaptable) element).getAdapter(IProject.class); 115 } 116 117 if (project != null && !projects.contains(project)) { 118 projects.add(project); 119 } 120 } 121 } 122 123 if (projects.isEmpty()) { 124 // Try to look at the active editor instead 125 IFile file = AdtUtils.getActiveFile(); 126 if (file != null) { 127 projects.add(file.getProject()); 128 } 129 } 130 131 if (projects.isEmpty()) { 132 // If we didn't find a default project based on the selection, check how many 133 // open Android projects we can find in the current workspace. If there's only 134 // one, we'll just select it by default. 135 IJavaProject[] open = AdtUtils.getOpenAndroidProjects(); 136 for (IJavaProject project : open) { 137 projects.add(project.getProject()); 138 } 139 } else { 140 // Make sure all the projects are Android projects 141 for (IProject project : projects) { 142 if (!BaseProjectHelper.isAndroidProject(project)) { 143 if (warn) { 144 MessageDialog.openWarning(AdtPlugin.getDisplay().getActiveShell(), 145 "Lint", "Select Android projects."); 146 } 147 return Collections.emptyList(); 148 } 149 } 150 } 151 152 if (projects.isEmpty() && warn) { 153 MessageDialog.openWarning(AdtPlugin.getDisplay().getActiveShell(), "Lint", 154 "Could not run Lint: Select a project first."); 155 } 156 157 return projects; 158 } 159 160 @Override setActivePart(IAction action, IWorkbenchPart targetPart)161 public void setActivePart(IAction action, IWorkbenchPart targetPart) { 162 } 163 164 @Override dispose()165 public void dispose() { 166 if (mMenu != null) { 167 mMenu.dispose(); 168 } 169 } 170 171 @Override init(IWorkbenchWindow window)172 public void init(IWorkbenchWindow window) { 173 } 174 175 // ---- IMenuCreator ---- 176 177 @Override getMenu(Control parent)178 public Menu getMenu(Control parent) { 179 mMenu = new Menu(parent); 180 181 IconFactory iconFactory = IconFactory.getInstance(); 182 ImageDescriptor allIcon = iconFactory.getImageDescriptor("lintrun"); //$NON-NLS-1$ 183 LintMenuAction allAction = new LintMenuAction("Check All Projects", allIcon, false, null); 184 185 addAction(allAction); 186 addSeparator(); 187 IJavaProject[] projects = AdtUtils.getOpenAndroidProjects(); 188 ILabelProvider provider = new JavaElementLabelProvider( 189 JavaElementLabelProvider.SHOW_DEFAULT); 190 for (IJavaProject project : projects) { 191 IProject p = project.getProject(); 192 ImageDescriptor icon = ImageDescriptor.createFromImage(provider.getImage(p)); 193 String label = String.format("Check %1$s", p.getName()); 194 LintMenuAction projectAction = new LintMenuAction(label, icon, false, p); 195 addAction(projectAction); 196 } 197 198 ITextEditor textEditor = AdtUtils.getActiveTextEditor(); 199 if (textEditor != null) { 200 IFile file = AdtUtils.getActiveFile(); 201 // Currently only supported for XML files 202 if (file != null && LintUtils.endsWith(file.getName(), DOT_XML)) { 203 ImageDescriptor icon = ImageDescriptor.createFromImage(provider.getImage(file)); 204 IAction fileAction = new LintMenuAction("Check Current File", icon, false, file); 205 206 addSeparator(); 207 addAction(fileAction); 208 } 209 } 210 211 ISharedImages images = PlatformUI.getWorkbench().getSharedImages(); 212 ImageDescriptor clear = images.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVEALL); 213 LintMenuAction clearAction = new LintMenuAction("Clear Lint Markers", clear, true, null); 214 addSeparator(); 215 addAction(clearAction); 216 217 return mMenu; 218 } 219 addAction(IAction action)220 private void addAction(IAction action) { 221 ActionContributionItem item = new ActionContributionItem(action); 222 item.fill(mMenu, -1); 223 } 224 addSeparator()225 private void addSeparator() { 226 new Separator().fill(mMenu, -1); 227 } 228 229 @Override getMenu(Menu parent)230 public Menu getMenu(Menu parent) { 231 return null; 232 } 233 234 /** 235 * Actions in the pulldown context menu: run lint or clear lint markers on 236 * the given resource 237 */ 238 private static class LintMenuAction extends Action { 239 private final boolean mClear; 240 private final IResource mResource; 241 242 /** 243 * Creates a new context menu action 244 * 245 * @param text the label 246 * @param descriptor the icon 247 * @param clear if true, clear lint markers otherwise check the resource 248 * @param resource the resource to check or clear markers for, where 249 * null means all projects 250 */ LintMenuAction(String text, ImageDescriptor descriptor, boolean clear, IResource resource)251 private LintMenuAction(String text, ImageDescriptor descriptor, boolean clear, 252 IResource resource) { 253 super(text, descriptor); 254 mClear = clear; 255 mResource = resource; 256 } 257 258 @Override run()259 public void run() { 260 List<IResource> resources = new ArrayList<IResource>(); 261 if (mResource == null) { 262 // All projects 263 IJavaProject[] open = AdtUtils.getOpenAndroidProjects(); 264 for (IJavaProject project : open) { 265 resources.add(project.getProject()); 266 } 267 } else { 268 resources.add(mResource); 269 } 270 EclipseLintRunner.cancelCurrentJobs(false); 271 if (mClear) { 272 EclipseLintClient.clearMarkers(resources); 273 } else { 274 EclipseLintRunner.startLint(resources, null, false /*fatalOnly*/, true /*show*/); 275 } 276 } 277 } 278 } 279