1 /* 2 * Copyright (C) 2012 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 package com.android.ide.eclipse.adt.internal.lint; 17 18 import static com.android.SdkConstants.DOT_CLASS; 19 import static com.android.SdkConstants.DOT_JAVA; 20 import static com.android.SdkConstants.DOT_XML; 21 22 import com.android.SdkConstants; 23 import com.android.annotations.NonNull; 24 import com.android.annotations.Nullable; 25 import com.android.ide.eclipse.adt.AdtPlugin; 26 import com.android.ide.eclipse.adt.AdtUtils; 27 import com.android.tools.lint.client.api.IssueRegistry; 28 import com.android.tools.lint.client.api.LintDriver; 29 import com.android.tools.lint.client.api.LintRequest; 30 import com.android.tools.lint.detector.api.Issue; 31 import com.android.tools.lint.detector.api.Scope; 32 import com.android.utils.SdkUtils; 33 34 import org.eclipse.core.resources.IFile; 35 import org.eclipse.core.resources.IMarker; 36 import org.eclipse.core.resources.IProject; 37 import org.eclipse.core.resources.IResource; 38 import org.eclipse.core.runtime.IProgressMonitor; 39 import org.eclipse.core.runtime.IStatus; 40 import org.eclipse.core.runtime.Status; 41 import org.eclipse.core.runtime.jobs.IJobManager; 42 import org.eclipse.core.runtime.jobs.Job; 43 44 import java.io.File; 45 import java.util.ArrayList; 46 import java.util.EnumSet; 47 import java.util.List; 48 49 /** Job to check lint on a set of resources */ 50 public final class LintJob extends Job { 51 /** Job family */ 52 private static final Object FAMILY_RUN_LINT = new Object(); 53 private final EclipseLintClient mClient; 54 private final List<? extends IResource> mResources; 55 private final IResource mSource; 56 private final IssueRegistry mRegistry; 57 private LintDriver mLint; 58 private boolean mFatal; 59 LintJob( @onNull EclipseLintClient client, @NonNull List<? extends IResource> resources, @Nullable IResource source, @NonNull IssueRegistry registry)60 public LintJob( 61 @NonNull EclipseLintClient client, 62 @NonNull List<? extends IResource> resources, 63 @Nullable IResource source, 64 @NonNull IssueRegistry registry) { 65 super("Running Android Lint"); 66 mClient = client; 67 mResources = resources; 68 mSource = source; 69 mRegistry = registry; 70 } 71 LintJob( @onNull EclipseLintClient client, @NonNull List<? extends IResource> resources, @Nullable IResource source)72 public LintJob( 73 @NonNull EclipseLintClient client, 74 @NonNull List<? extends IResource> resources, 75 @Nullable IResource source) { 76 this(client, resources, source, EclipseLintClient.getRegistry()); 77 } 78 79 @Override belongsTo(Object family)80 public boolean belongsTo(Object family) { 81 return family == FAMILY_RUN_LINT; 82 } 83 84 @Override canceling()85 protected void canceling() { 86 super.canceling(); 87 if (mLint != null) { 88 mLint.cancel(); 89 } 90 } 91 92 @Override 93 @NonNull run(IProgressMonitor monitor)94 protected IStatus run(IProgressMonitor monitor) { 95 try { 96 monitor.beginTask("Looking for errors", IProgressMonitor.UNKNOWN); 97 EnumSet<Scope> scope = null; 98 List<File> files = new ArrayList<File>(mResources.size()); 99 for (IResource resource : mResources) { 100 File file = AdtUtils.getAbsolutePath(resource).toFile(); 101 files.add(file); 102 103 if (resource instanceof IProject && mSource == null) { 104 scope = Scope.ALL; 105 } else { 106 String name = resource.getName(); 107 if (SdkUtils.endsWithIgnoreCase(name, DOT_XML)) { 108 if (name.equals(SdkConstants.FN_ANDROID_MANIFEST_XML)) { 109 scope = EnumSet.of(Scope.MANIFEST); 110 } else { 111 scope = Scope.RESOURCE_FILE_SCOPE; 112 } 113 } else if (name.endsWith(DOT_JAVA) && resource instanceof IFile) { 114 if (scope != null) { 115 if (!scope.contains(Scope.JAVA_FILE)) { 116 scope = EnumSet.copyOf(scope); 117 scope.add(Scope.JAVA_FILE); 118 } 119 } else { 120 scope = Scope.JAVA_FILE_SCOPE; 121 } 122 } else if (name.endsWith(DOT_CLASS) && resource instanceof IFile) { 123 if (scope != null) { 124 if (!scope.contains(Scope.CLASS_FILE)) { 125 scope = EnumSet.copyOf(scope); 126 scope.add(Scope.CLASS_FILE); 127 } 128 } else { 129 scope = Scope.CLASS_FILE_SCOPE; 130 } 131 } else { 132 return new Status(Status.ERROR, AdtPlugin.PLUGIN_ID, Status.ERROR, 133 "Only XML & Java files are supported for single file lint", null); //$NON-NLS-1$ 134 } 135 } 136 } 137 if (scope == null) { 138 scope = Scope.ALL; 139 } 140 if (mSource == null) { 141 assert !Scope.checkSingleFile(scope) : scope + " with " + mResources; 142 } 143 // Check single file? 144 if (mSource != null) { 145 // Delete specific markers 146 IMarker[] markers = EclipseLintClient.getMarkers(mSource); 147 for (IMarker marker : markers) { 148 String id = marker.getAttribute(EclipseLintRunner.MARKER_CHECKID_PROPERTY, ""); 149 Issue issue = mRegistry.getIssue(id); 150 if (issue == null) { 151 continue; 152 } 153 if (issue.getImplementation().isAdequate(scope)) { 154 marker.delete(); 155 } 156 } 157 mClient.setSearchForSuperClasses(true); 158 } else { 159 EclipseLintClient.clearMarkers(mResources); 160 } 161 162 mLint = new LintDriver(mRegistry, mClient); 163 mLint.analyze(new LintRequest(mClient, files).setScope(scope)); 164 mFatal = mClient.hasFatalErrors(); 165 return Status.OK_STATUS; 166 } catch (Exception e) { 167 return new Status(Status.ERROR, AdtPlugin.PLUGIN_ID, Status.ERROR, 168 "Failed", e); //$NON-NLS-1$ 169 } finally { 170 if (monitor != null) { 171 monitor.done(); 172 } 173 } 174 } 175 176 /** 177 * Returns true if a fatal error was encountered 178 * 179 * @return true if a fatal error was encountered 180 */ isFatal()181 public boolean isFatal() { 182 return mFatal; 183 } 184 185 /** 186 * Returns the associated lint client 187 * 188 * @return the associated lint client 189 */ 190 @NonNull getLintClient()191 public EclipseLintClient getLintClient() { 192 return mClient; 193 } 194 195 /** Returns the current lint jobs, if any (never returns null but array may be empty) */ 196 @NonNull getCurrentJobs()197 static Job[] getCurrentJobs() { 198 IJobManager jobManager = Job.getJobManager(); 199 return jobManager.find(LintJob.FAMILY_RUN_LINT); 200 } 201 } 202