• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.tools.lint.detector.api;
18 
19 import com.android.annotations.NonNull;
20 import com.android.annotations.Nullable;
21 import com.android.tools.lint.client.api.Configuration;
22 import com.android.tools.lint.client.api.LintClient;
23 import com.android.tools.lint.client.api.LintDriver;
24 import com.android.tools.lint.client.api.SdkInfo;
25 import com.google.common.annotations.Beta;
26 
27 import java.io.File;
28 import java.util.EnumSet;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.concurrent.atomic.AtomicBoolean;
32 
33 /**
34  * Context passed to the detectors during an analysis run. It provides
35  * information about the file being analyzed, it allows shared properties (so
36  * the detectors can share results), etc.
37  * <p>
38  * <b>NOTE: This is not a public or final API; if you rely on this be prepared
39  * to adjust your code for the next tools release.</b>
40  */
41 @Beta
42 public class Context {
43     /**
44      * The file being checked. Note that this may not always be to a concrete
45      * file. For example, in the {@link Detector#beforeCheckProject(Context)}
46      * method, the context file is the directory of the project.
47      */
48     public final File file;
49 
50     /** The driver running through the checks */
51     protected final LintDriver mDriver;
52 
53     /** The project containing the file being checked */
54     @NonNull
55     private final Project mProject;
56 
57     /**
58      * The "main" project. For normal projects, this is the same as {@link #mProject},
59      * but for library projects, it's the root project that includes (possibly indirectly)
60      * the various library projects and their library projects.
61      * <p>
62      * Note that this is a property on the {@link Context}, not the
63      * {@link Project}, since a library project can be included from multiple
64      * different top level projects, so there isn't <b>one</b> main project,
65      * just one per main project being analyzed with its library projects.
66      */
67     private final Project mMainProject;
68 
69     /** The current configuration controlling which checks are enabled etc */
70     private final Configuration mConfiguration;
71 
72     /** The contents of the file */
73     private String mContents;
74 
75     /**
76      * Whether the lint job has been canceled.
77      * <p>
78      * Slow-running detectors should check this flag via
79      * {@link AtomicBoolean#get()} and abort if canceled
80      */
81     @NonNull
82     public final AtomicBoolean canceled = new AtomicBoolean();
83 
84     /** Map of properties to share results between detectors */
85     private Map<String, Object> mProperties;
86 
87     /**
88      * Construct a new {@link Context}
89      *
90      * @param driver the driver running through the checks
91      * @param project the project containing the file being checked
92      * @param main the main project if this project is a library project, or
93      *            null if this is not a library project. The main project is
94      *            the root project of all library projects, not necessarily the
95      *            directly including project.
96      * @param file the file being checked
97      */
Context( @onNull LintDriver driver, @NonNull Project project, @Nullable Project main, @NonNull File file)98     public Context(
99             @NonNull LintDriver driver,
100             @NonNull Project project,
101             @Nullable Project main,
102             @NonNull File file) {
103         this.file = file;
104 
105         mDriver = driver;
106         mProject = project;
107         mMainProject = main;
108         mConfiguration = project.getConfiguration();
109     }
110 
111     /**
112      * Returns the scope for the lint job
113      *
114      * @return the scope, never null
115      */
116     @NonNull
getScope()117     public EnumSet<Scope> getScope() {
118         return mDriver.getScope();
119     }
120 
121     /**
122      * Returns the configuration for this project.
123      *
124      * @return the configuration, never null
125      */
126     @NonNull
getConfiguration()127     public Configuration getConfiguration() {
128         return mConfiguration;
129     }
130 
131     /**
132      * Returns the project containing the file being checked
133      *
134      * @return the project, never null
135      */
136     @NonNull
getProject()137     public Project getProject() {
138         return mProject;
139     }
140 
141     /**
142      * Returns the main project if this project is a library project, or self
143      * if this is not a library project. The main project is the root project
144      * of all library projects, not necessarily the directly including project.
145      *
146      * @return the main project, never null
147      */
148     @NonNull
getMainProject()149     public Project getMainProject() {
150         return mMainProject != null ? mMainProject : mProject;
151     }
152 
153     /**
154      * Returns the lint client requesting the lint check
155      *
156      * @return the client, never null
157      */
158     @NonNull
getClient()159     public LintClient getClient() {
160         return mDriver.getClient();
161     }
162 
163     /**
164      * Returns the driver running through the lint checks
165      *
166      * @return the driver
167      */
168     @NonNull
getDriver()169     public LintDriver getDriver() {
170         return mDriver;
171     }
172 
173     /**
174      * Returns the contents of the file. This may not be the contents of the
175      * file on disk, since it delegates to the {@link LintClient}, which in turn
176      * may decide to return the current edited contents of the file open in an
177      * editor.
178      *
179      * @return the contents of the given file, or null if an error occurs.
180      */
181     @Nullable
getContents()182     public String getContents() {
183         if (mContents == null) {
184             mContents = mDriver.getClient().readFile(file);
185         }
186 
187         return mContents;
188     }
189 
190     /**
191      * Returns the value of the given named property, or null.
192      *
193      * @param name the name of the property
194      * @return the corresponding value, or null
195      */
196     @Nullable
getProperty(String name)197     public Object getProperty(String name) {
198         if (mProperties == null) {
199             return null;
200         }
201 
202         return mProperties.get(name);
203     }
204 
205     /**
206      * Sets the value of the given named property.
207      *
208      * @param name the name of the property
209      * @param value the corresponding value
210      */
setProperty(@onNull String name, @Nullable Object value)211     public void setProperty(@NonNull String name, @Nullable Object value) {
212         if (value == null) {
213             if (mProperties != null) {
214                 mProperties.remove(name);
215             }
216         } else {
217             if (mProperties == null) {
218                 mProperties = new HashMap<String, Object>();
219             }
220             mProperties.put(name, value);
221         }
222     }
223 
224     /**
225      * Gets the SDK info for the current project.
226      *
227      * @return the SDK info for the current project, never null
228      */
229     @NonNull
getSdkInfo()230     public SdkInfo getSdkInfo() {
231         return mProject.getSdkInfo();
232     }
233 
234     // ---- Convenience wrappers  ---- (makes the detector code a bit leaner)
235 
236     /**
237      * Returns false if the given issue has been disabled. Convenience wrapper
238      * around {@link Configuration#getSeverity(Issue)}.
239      *
240      * @param issue the issue to check
241      * @return false if the issue has been disabled
242      */
isEnabled(@onNull Issue issue)243     public boolean isEnabled(@NonNull Issue issue) {
244         return mConfiguration.isEnabled(issue);
245     }
246 
247     /**
248      * Reports an issue. Convenience wrapper around {@link LintClient#report}
249      *
250      * @param issue the issue to report
251      * @param location the location of the issue, or null if not known
252      * @param message the message for this warning
253      * @param data any associated data, or null
254      */
report( @onNull Issue issue, @Nullable Location location, @NonNull String message, @Nullable Object data)255     public void report(
256             @NonNull Issue issue,
257             @Nullable Location location,
258             @NonNull String message,
259             @Nullable Object data) {
260         Configuration configuration = mConfiguration;
261 
262         // If this error was computed for a context where the context corresponds to
263         // a project instead of a file, the actual error may be in a different project (e.g.
264         // a library project), so adjust the configuration as necessary.
265         if (location != null && location.getFile() != null) {
266             Project project = mDriver.findProjectFor(location.getFile());
267             if (project != null) {
268                 configuration = project.getConfiguration();
269             }
270         }
271 
272         // If an error occurs in a library project, but you've disabled that check in the
273         // main project, disable it in the library project too. (In some cases you don't
274         // control the lint.xml of a library project, and besides, if you're not interested in
275         // a check for your main project you probably don't care about it in the library either.)
276         if (configuration != mConfiguration
277                 && mConfiguration.getSeverity(issue) == Severity.IGNORE) {
278             return;
279         }
280 
281         Severity severity = configuration.getSeverity(issue);
282         if (severity == Severity.IGNORE) {
283             return;
284         }
285 
286         mDriver.getClient().report(this, issue, severity, location, message, data);
287     }
288 
289     /**
290      * Send an exception to the log. Convenience wrapper around {@link LintClient#log}.
291      *
292      * @param exception the exception, possibly null
293      * @param format the error message using {@link String#format} syntax, possibly null
294      * @param args any arguments for the format string
295      */
log( @ullable Throwable exception, @Nullable String format, @Nullable Object... args)296     public void log(
297             @Nullable Throwable exception,
298             @Nullable String format,
299             @Nullable Object... args) {
300         mDriver.getClient().log(exception, format, args);
301     }
302 
303     /**
304      * Returns the current phase number. The first pass is numbered 1. Only one pass
305      * will be performed, unless a {@link Detector} calls {@link #requestRepeat}.
306      *
307      * @return the current phase, usually 1
308      */
getPhase()309     public int getPhase() {
310         return mDriver.getPhase();
311     }
312 
313     /**
314      * Requests another pass through the data for the given detector. This is
315      * typically done when a detector needs to do more expensive computation,
316      * but it only wants to do this once it <b>knows</b> that an error is
317      * present, or once it knows more specifically what to check for.
318      *
319      * @param detector the detector that should be included in the next pass.
320      *            Note that the lint runner may refuse to run more than a couple
321      *            of runs.
322      * @param scope the scope to be revisited. This must be a subset of the
323      *       current scope ({@link #getScope()}, and it is just a performance hint;
324      *       in particular, the detector should be prepared to be called on other
325      *       scopes as well (since they may have been requested by other detectors).
326      *       You can pall null to indicate "all".
327      */
requestRepeat(@onNull Detector detector, @Nullable EnumSet<Scope> scope)328     public void requestRepeat(@NonNull Detector detector, @Nullable EnumSet<Scope> scope) {
329         mDriver.requestRepeat(detector, scope);
330     }
331 }
332