Home | History | Annotate | Download | only in lint
      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 
     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 
     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
     80     public boolean belongsTo(Object family) {
     81         return family == FAMILY_RUN_LINT;
     82     }
     83 
     84     @Override
     85     protected void canceling() {
     86         super.canceling();
     87         if (mLint != null) {
     88             mLint.cancel();
     89         }
     90     }
     91 
     92     @Override
     93     @NonNull
     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      */
    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
    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
    197     static Job[] getCurrentJobs() {
    198         IJobManager jobManager = Job.getJobManager();
    199         return jobManager.find(LintJob.FAMILY_RUN_LINT);
    200     }
    201 }
    202