blob: e1054b12bde24064e73cb1697a11cc99d4acd154 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.detector.api;
import com.android.annotations.NonNull;
import java.util.EnumSet;
/**
* An {@linkplain Implementation} of an {@link Issue} maps to the {@link Detector} class responsible
* for analyzing the issue, as well as the {@link Scope} required by the detector to perform its
* analysis.
*/
public class Implementation {
private final Class<? extends Detector> detectorClass;
private final EnumSet<Scope> scope;
private final EnumSet<Scope>[] analysisScopes;
@SuppressWarnings("unchecked")
private static final EnumSet<Scope>[] EMPTY = new EnumSet[0];
/**
* Creates a new implementation for analyzing one or more issues
*
* @param detectorClass the class of the detector to find this issue
* @param scope the scope of files required to analyze this issue
*/
@SuppressWarnings("unchecked")
public Implementation(
@NonNull Class<? extends Detector> detectorClass, @NonNull EnumSet<Scope> scope) {
this(detectorClass, scope, EMPTY);
}
/**
* Creates a new implementation for analyzing one or more issues
*
* @param detectorClass the class of the detector to find this issue
* @param scope the scope of files required to analyze this issue
* @param analysisScopes optional set of extra scopes the detector is capable of working in
*/
@SafeVarargs
public Implementation(
@NonNull Class<? extends Detector> detectorClass,
@NonNull EnumSet<Scope> scope,
@NonNull EnumSet<Scope>... analysisScopes) {
this.detectorClass = detectorClass;
this.scope = scope;
this.analysisScopes = analysisScopes;
}
/**
* Returns the class of the detector to use to find this issue
*
* @return the class of the detector to use to find this issue
*/
@NonNull
public Class<? extends Detector> getDetectorClass() {
return detectorClass;
}
@Override
public String toString() {
return detectorClass.toString();
}
/**
* Returns the scope required to analyze the code to detect this issue. This is determined by
* the detectors which reports the issue.
*
* @return the required scope
*/
@NonNull
public EnumSet<Scope> getScope() {
return scope;
}
/**
* Returns the sets of scopes required to analyze this issue, or null if all scopes named by
* {@link #getScope()} are necessary. Note that only <b>one</b> match out of this collection is
* required, not all, and that the scope set returned by {@link #getScope()} does not have to be
* returned by this method, but is always implied to be included.
*
* <p>The scopes returned by {@link #getScope()} list all the various scopes that are
* <b>affected</b> by this issue, meaning the detector should consider it. Frequently, the
* detector must analyze all these scopes in order to properly decide whether an issue is found.
* For example, the unused resource detector needs to consider both the XML resource files and
* the Java source files in order to decide if a resource is unused. If it analyzes just the
* Java files for example, it might incorrectly conclude that a resource is unused because it
* did not discover a resource reference in an XML file.
*
* <p>However, there are other issues where the issue can occur in a variety of files, but the
* detector can consider each in isolation. For example, the API checker is affected by both XML
* files and Java class files (detecting both layout constructor references in XML layout files
* as well as code references in .class files). It doesn't have to analyze both; it is capable
* of incrementally analyzing just an XML file, or just a class file, without considering the
* other.
*
* <p>The required scope list provides a list of scope sets that can be used to analyze this
* issue. For each scope set, all the scopes must be matched by the incremental analysis, but
* any one of the scope sets can be analyzed in isolation.
*
* <p>The required scope list is not required to include the full scope set returned by {@link
* #getScope()}; that set is always assumed to be included.
*
* <p>NOTE: You would normally call {@link #isAdequate(EnumSet)} rather than calling this method
* directly.
*
* @return a list of required scopes.
*/
@NonNull
public EnumSet<Scope>[] getAnalysisScopes() {
return analysisScopes;
}
/**
* Returns true if the given scope is adequate for analyzing this issue. This looks through the
* analysis scopes (see {@link #getAnalysisScopes()}) and if the scope passed in fully covers at
* least one of them, or if it covers the scope of the issue itself (see {@link #getScope()},
* which should be a superset of all the analysis scopes) returns true.
*
* <p>The scope set returned by {@link #getScope()} lists all the various scopes that are
* <b>affected</b> by this issue, meaning the detector should consider it. Frequently, the
* detector must analyze all these scopes in order to properly decide whether an issue is found.
* For example, the unused resource detector needs to consider both the XML resource files and
* the Java source files in order to decide if a resource is unused. If it analyzes just the
* Java files for example, it might incorrectly conclude that a resource is unused because it
* did not discover a resource reference in an XML file.
*
* <p>However, there are other issues where the issue can occur in a variety of files, but the
* detector can consider each in isolation. For example, the API checker is affected by both XML
* files and Java class files (detecting both layout constructor references in XML layout files
* as well as code references in .class files). It doesn't have to analyze both; it is capable
* of incrementally analyzing just an XML file, or just a class file, without considering the
* other.
*
* <p>An issue can register additional scope sets that can are adequate for analyzing the issue,
* by supplying it to {@link #Implementation(Class, java.util.EnumSet, java.util.EnumSet[])}.
* This method returns true if the given scope matches one or more analysis scope, or the
* overall scope.
*
* @param scope the scope available for analysis
* @return true if this issue can be analyzed with the given available scope
*/
public boolean isAdequate(@NonNull EnumSet<Scope> scope) {
if (scope.containsAll(this.scope)) {
return true;
}
if (analysisScopes != null) {
for (EnumSet<Scope> analysisScope : analysisScopes) {
if (scope.containsAll(analysisScope)) {
return true;
}
}
}
if (this.scope.size() == scope.size() + 1
&& !scope.contains(Scope.TEST_SOURCES)
&& this.scope.contains(Scope.TEST_SOURCES)) {
// TEST_SOURCES is a special marker scope
return this.scope.contains(scope.iterator().next());
}
return false;
}
}