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