• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.ide.eclipse.adt.internal.lint;
17 
18 
19 import com.android.annotations.NonNull;
20 import com.android.annotations.Nullable;
21 import com.android.ide.eclipse.adt.AdtPlugin;
22 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
23 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
24 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
25 import com.android.tools.lint.client.api.IssueRegistry;
26 
27 import org.eclipse.core.resources.IProject;
28 import org.eclipse.core.resources.IResource;
29 import org.eclipse.core.runtime.jobs.Job;
30 import org.eclipse.jface.dialogs.MessageDialog;
31 import org.eclipse.jface.text.IDocument;
32 import org.eclipse.swt.widgets.Shell;
33 
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.IdentityHashMap;
37 import java.util.List;
38 import java.util.Map;
39 
40 /**
41  * Eclipse implementation for running lint on workspace files and projects.
42  */
43 public class EclipseLintRunner {
44     static final String MARKER_CHECKID_PROPERTY = "checkid";    //$NON-NLS-1$
45 
46     /**
47      * Runs lint and updates the markers, and waits for the result. Returns
48      * true if fatal errors were found.
49      *
50      * @param resources the resources (project, folder or file) to be analyzed
51      * @param source if checking a single source file, the source file
52      * @param doc the associated document, if known, or null
53      * @param fatalOnly if true, only report fatal issues (severity=error)
54      * @return true if any fatal errors were encountered.
55      */
runLint( @onNull List<? extends IResource> resources, @Nullable IResource source, @Nullable IDocument doc, boolean fatalOnly)56     private static boolean runLint(
57             @NonNull List<? extends IResource> resources,
58             @Nullable IResource source,
59             @Nullable IDocument doc,
60             boolean fatalOnly) {
61         resources = addLibraries(resources);
62         LintJob job = (LintJob) startLint(resources, source,  doc, fatalOnly,
63                 false /*show*/);
64         try {
65             job.join();
66             boolean fatal = job.isFatal();
67 
68             if (fatal) {
69                 LintViewPart.show(resources);
70             }
71 
72             return fatal;
73         } catch (InterruptedException e) {
74             AdtPlugin.log(e, null);
75         }
76 
77         return false;
78     }
79 
80     /**
81      * Runs lint and updates the markers. Does not wait for the job to finish -
82      * just returns immediately.
83      *
84      * @param resources the resources (project, folder or file) to be analyzed
85      * @param source if checking a single source file, the source file. When
86      *            single checking an XML file, this is typically the same as the
87      *            file passed in the list in the first parameter, but when
88      *            checking the .class files of a Java file for example, the
89      *            .class file and all the inner classes of the Java file are
90      *            passed in the first parameter, and the corresponding .java
91      *            source file is passed here.
92      * @param doc the associated document, if known, or null
93      * @param fatalOnly if true, only report fatal issues (severity=error)
94      * @param show if true, show the results in a {@link LintViewPart}
95      * @return the job running lint in the background.
96      */
startLint( @onNull List<? extends IResource> resources, @Nullable IResource source, @Nullable IDocument doc, boolean fatalOnly, boolean show)97     public static Job startLint(
98             @NonNull List<? extends IResource> resources,
99             @Nullable IResource source,
100             @Nullable IDocument doc,
101             boolean fatalOnly,
102             boolean show) {
103         IssueRegistry registry = EclipseLintClient.getRegistry();
104         EclipseLintClient client = new EclipseLintClient(registry, resources, doc, fatalOnly);
105         return startLint(client, resources, source, show);
106     }
107 
108     /**
109      * Runs lint and updates the markers. Does not wait for the job to finish -
110      * just returns immediately.
111      *
112      * @param client the lint client receiving issue reports etc
113      * @param resources the resources (project, folder or file) to be analyzed
114      * @param source if checking a single source file, the source file. When
115      *            single checking an XML file, this is typically the same as the
116      *            file passed in the list in the first parameter, but when
117      *            checking the .class files of a Java file for example, the
118      *            .class file and all the inner classes of the Java file are
119      *            passed in the first parameter, and the corresponding .java
120      *            source file is passed here.
121      * @param show if true, show the results in a {@link LintViewPart}
122      * @return the job running lint in the background.
123      */
startLint( @onNull EclipseLintClient client, @NonNull List<? extends IResource> resources, @Nullable IResource source, boolean show)124     public static Job startLint(
125             @NonNull EclipseLintClient client,
126             @NonNull List<? extends IResource> resources,
127             @Nullable IResource source,
128             boolean show) {
129         if (resources != null && !resources.isEmpty()) {
130             if (!AdtPrefs.getPrefs().getSkipLibrariesFromLint()) {
131                 resources = addLibraries(resources);
132             }
133 
134             cancelCurrentJobs(false);
135 
136             LintJob job = new LintJob(client, resources, source);
137             job.schedule();
138 
139             if (show) {
140                 // Show lint view where the results are listed
141                 LintViewPart.show(resources);
142             }
143             return job;
144         }
145 
146         return null;
147     }
148 
149     /**
150      * Run Lint for an Export APK action. If it succeeds (no fatal errors)
151      * returns true, and if it fails it will display an error message and return
152      * false.
153      *
154      * @param shell the parent shell to show error messages in
155      * @param project the project to run lint on
156      * @return true if the lint run succeeded with no fatal errors
157      */
runLintOnExport(Shell shell, IProject project)158     public static boolean runLintOnExport(Shell shell, IProject project) {
159         if (AdtPrefs.getPrefs().isLintOnExport()) {
160             boolean fatal = EclipseLintRunner.runLint(Collections.singletonList(project),
161                     null, null, true /*fatalOnly*/);
162             if (fatal) {
163                 MessageDialog.openWarning(shell,
164                         "Export Aborted",
165                         "Export aborted because fatal lint errors were found. These " +
166                         "are listed in the Lint View. Either fix these before " +
167                         "running Export again, or turn off \"Run full error check " +
168                         "when exporting app\" in the Android > Lint Error Checking " +
169                         "preference page.");
170                 return false;
171             }
172         }
173 
174         return true;
175     }
176 
177     /** Cancels the current lint jobs, if any, and optionally waits for them to finish */
cancelCurrentJobs(boolean wait)178     static void cancelCurrentJobs(boolean wait) {
179         // Cancel any current running jobs first
180         Job[] currentJobs = LintJob.getCurrentJobs();
181         for (Job job : currentJobs) {
182             job.cancel();
183         }
184 
185         if (wait) {
186             for (Job job : currentJobs) {
187                 try {
188                     job.join();
189                 } catch (InterruptedException e) {
190                     AdtPlugin.log(e, null);
191                 }
192             }
193         }
194     }
195 
196     /** If the resource list contains projects, add in any library projects as well */
addLibraries(List<? extends IResource> resources)197     private static List<? extends IResource> addLibraries(List<? extends IResource> resources) {
198         if (resources != null && !resources.isEmpty()) {
199             boolean haveProjects = false;
200             for (IResource resource : resources) {
201                 if (resource instanceof IProject) {
202                     haveProjects = true;
203                     break;
204                 }
205             }
206 
207             if (haveProjects) {
208                 List<IResource> result = new ArrayList<IResource>();
209                 Map<IProject, IProject> allProjects = new IdentityHashMap<IProject, IProject>();
210                 List<IProject> projects = new ArrayList<IProject>();
211                 for (IResource resource : resources) {
212                     if (resource instanceof IProject) {
213                         IProject project = (IProject) resource;
214                         allProjects.put(project, project);
215                         projects.add(project);
216                     } else {
217                         result.add(resource);
218                     }
219                 }
220                 for (IProject project : projects) {
221                     ProjectState state = Sdk.getProjectState(project);
222                     if (state != null) {
223                         for (IProject library : state.getFullLibraryProjects()) {
224                             allProjects.put(library, library);
225                         }
226                     }
227                 }
228                 for (IProject project : allProjects.keySet()) {
229                     result.add(project);
230                 }
231 
232                 return result;
233             }
234         }
235 
236         return resources;
237     }
238 }
239