blob: 836ab6cdd58a257617af563509d83cd59fc19655 [file] [log] [blame]
/*
* Copyright (C) 2011 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.SdkConstants.ANDROID_MANIFEST_XML
import com.android.SdkConstants.DOT_CLASS
import com.android.SdkConstants.DOT_GRADLE
import com.android.SdkConstants.DOT_JAVA
import com.android.SdkConstants.DOT_KT
import com.android.SdkConstants.DOT_KTS
import com.android.SdkConstants.DOT_PNG
import com.android.SdkConstants.DOT_PROPERTIES
import com.android.SdkConstants.DOT_WEBP
import com.android.SdkConstants.DOT_XML
import com.android.SdkConstants.FN_PROJECT_PROGUARD_FILE
import com.android.SdkConstants.OLD_PROGUARD_FILE
import com.android.SdkConstants.RES_FOLDER
import com.google.common.annotations.Beta
import java.util.EnumSet
/**
* The scope of a detector is the set of files a detector must consider when
* performing its analysis. This can be used to determine when issues are
* potentially obsolete, whether a detector should re-run on a file save, etc.
*
*
* **NOTE: This is not a public or final API; if you rely on this be prepared
* to adjust your code for the next tools release.**
*/
@Beta
enum class Scope {
/**
* The analysis only considers a single XML resource file at a time.
*
* Issues which are only affected by a single resource file can be checked
* for incrementally when a file is edited.
*/
RESOURCE_FILE,
/**
* The analysis only considers a single binary (typically a bitmap) resource file at a time.
*
* Issues which are only affected by a single resource file can be checked
* for incrementally when a file is edited.
*/
BINARY_RESOURCE_FILE,
/**
* The analysis considers the resource folders (which also includes asset folders)
*/
RESOURCE_FOLDER,
/**
* The analysis considers **all** the resource file. This scope must not
* be used in conjunction with [.RESOURCE_FILE]; an issue scope is
* either considering just a single resource file or all the resources, not
* both.
*/
ALL_RESOURCE_FILES,
/**
* The analysis only considers a single Java or Kotlin source file at a time.
*
* Issues which are only affected by a single Java/Kotlin source file can be
* checked for incrementally when a Java/Kotlin source file is edited.
*/
JAVA_FILE,
/**
* The analysis considers **all** the Java and Kotlin source files together.
*
* This flag is mutually exclusive with [.JAVA_FILE].
*/
ALL_JAVA_FILES,
/**
* The analysis only considers a single Java/Kotlin class file at a time.
*
* Issues which are only affected by a single Java/Kotlin class file can be checked
* for incrementally when a Java/Kotlin source file is edited and then recompiled.
*/
CLASS_FILE,
/**
* The analysis considers **all** the Java/Kotlin class files together.
*
* This flag is mutually exclusive with [.CLASS_FILE].
*/
ALL_CLASS_FILES,
/** The analysis considers the manifest file */
MANIFEST,
/** The analysis considers the Proguard configuration file */
PROGUARD_FILE,
/**
* The analysis considers classes in the libraries for this project. These
* will be analyzed before the classes themselves. NOTE: This excludes
* provided libraries.
*/
JAVA_LIBRARIES,
/** The analysis considers a Gradle build file */
GRADLE_FILE,
/** The analysis considers Java property files */
PROPERTY_FILE,
/** The analysis considers test sources as well */
TEST_SOURCES,
/**
* Scope for other files. Issues that specify a custom scope will be called unconditionally.
* This will call [Detector.run]} on the detectors unconditionally.
*/
OTHER;
companion object {
/**
* Returns true if the given scope set corresponds to scanning a single file
* rather than a whole project
*
* @param scopes the scope set to check
*
* @return true if the scope set references a single file
*/
@JvmStatic
fun checkSingleFile(scopes: EnumSet<Scope>): Boolean {
val size = scopes.size
if (size > 5) {
return false
}
var expected = 1
if (scopes.contains(TEST_SOURCES)) {
expected++
}
if (scopes.contains(CLASS_FILE) && scopes.contains(JAVA_FILE)) {
// When single checking a Java source file, we check both its Java source
// and the associated class files
expected++
}
// Filter out non-file-type scopes
return size == expected &&
(scopes.contains(JAVA_FILE) ||
scopes.contains(RESOURCE_FILE) ||
scopes.contains(BINARY_RESOURCE_FILE) ||
scopes.contains(GRADLE_FILE) ||
scopes.contains(CLASS_FILE) ||
scopes.contains(PROGUARD_FILE) ||
scopes.contains(PROPERTY_FILE) ||
scopes.contains(MANIFEST))
}
/**
* Returns the intersection of two scope sets.
* The returned set is deliberately mutable by the caller.
*
* @param scope1 the first set to intersect
*
* @param scope2 the second set to intersect
*
* @return the intersection of the two sets
*/
@JvmStatic
fun intersect(
scope1: EnumSet<Scope>,
scope2: EnumSet<Scope>
): EnumSet<Scope> {
val scope = EnumSet.copyOf(scope1)
scope.retainAll(scope2)
return scope
}
/**
* Infers a suitable scope to use from the given projects to be analyzed
* @param projects the projects to find a suitable scope for
*
* @return the scope to use
*/
@JvmStatic
fun infer(projects: Collection<Project>?): EnumSet<Scope> {
if (projects == null || projects.isEmpty()) {
return Scope.ALL
}
// Infer the scope
var scope = EnumSet.noneOf(Scope::class.java)
for (project in projects) {
val subset = project.subset
if (subset != null) {
for (file in subset) {
val name = file.name
if (name == ANDROID_MANIFEST_XML) {
scope.add(MANIFEST)
} else if (name.endsWith(DOT_XML)) {
scope.add(RESOURCE_FILE)
} else if (name.endsWith(DOT_JAVA) || name.endsWith(DOT_KT)) {
scope.add(JAVA_FILE)
} else if (name.endsWith(DOT_CLASS)) {
scope.add(CLASS_FILE)
} else if (name.endsWith(DOT_GRADLE) || name.endsWith(DOT_KTS)) {
scope.add(GRADLE_FILE)
} else if (name == OLD_PROGUARD_FILE || name == FN_PROJECT_PROGUARD_FILE) {
scope.add(PROGUARD_FILE)
} else if (name.endsWith(DOT_PROPERTIES)) {
scope.add(PROPERTY_FILE)
} else if (name.endsWith(DOT_PNG) || name.endsWith(DOT_WEBP)) {
scope.add(BINARY_RESOURCE_FILE)
} else if (name == RES_FOLDER || file.parent == RES_FOLDER) {
scope.add(ALL_RESOURCE_FILES)
scope.add(RESOURCE_FILE)
scope.add(BINARY_RESOURCE_FILE)
scope.add(RESOURCE_FOLDER)
}
}
} else {
// Specified a full project: just use the full project scope
scope = Scope.ALL
break
}
}
return scope
}
/** All scopes: running lint on a project will check these scopes */
@JvmField
val ALL: EnumSet<Scope> = EnumSet.allOf(Scope::class.java)
/** Scope-set used for detectors which are affected by a single resource file */
@JvmField
val RESOURCE_FILE_SCOPE: EnumSet<Scope> = EnumSet.of(RESOURCE_FILE)
/** Scope-set used for detectors which are affected by a single resource folder */
@JvmField
val RESOURCE_FOLDER_SCOPE: EnumSet<Scope> = EnumSet.of(RESOURCE_FOLDER)
/** Scope-set used for detectors which scan all resources */
@JvmField
val ALL_RESOURCES_SCOPE: EnumSet<Scope> = EnumSet.of(ALL_RESOURCE_FILES)
/** Scope-set used for detectors which are affected by a single Java source file */
@JvmField
val JAVA_FILE_SCOPE: EnumSet<Scope> = EnumSet.of(JAVA_FILE)
/** Scope-set used for detectors which are affected by a single Java class file */
@JvmField
val CLASS_FILE_SCOPE: EnumSet<Scope> = EnumSet.of(CLASS_FILE)
/** Scope-set used for detectors which are affected by a single Gradle build file */
@JvmField
val GRADLE_SCOPE: EnumSet<Scope> = EnumSet.of(GRADLE_FILE)
/** Scope-set used for detectors which are affected by the manifest only */
@JvmField
val MANIFEST_SCOPE: EnumSet<Scope> = EnumSet.of(MANIFEST)
/** Scope-set used for detectors which correspond to some other context */
@JvmField
val OTHER_SCOPE: EnumSet<Scope> = EnumSet.of(OTHER)
/** Scope-set used for detectors which are affected by a single ProGuard class file */
@JvmField
val PROGUARD_SCOPE: EnumSet<Scope> = EnumSet.of(PROGUARD_FILE)
/** Scope-set used for detectors which correspond to property files */
@JvmField
val PROPERTY_SCOPE: EnumSet<Scope> = EnumSet.of(PROPERTY_FILE)
/** Resource XML files and manifest files */
@JvmField
val MANIFEST_AND_RESOURCE_SCOPE: EnumSet<Scope> =
EnumSet.of(Scope.MANIFEST, Scope.RESOURCE_FILE)
/** Scope-set used for detectors which are affected by single XML and Java source files */
@JvmField
val JAVA_AND_RESOURCE_FILES: EnumSet<Scope> = EnumSet.of(RESOURCE_FILE, JAVA_FILE)
/** Scope-set used for analyzing individual class files and all resource files */
@JvmField
val CLASS_AND_ALL_RESOURCE_FILES: EnumSet<Scope> =
EnumSet.of(ALL_RESOURCE_FILES, CLASS_FILE)
/** Scope-set used for analyzing all class files, including those in libraries */
@JvmField
val ALL_CLASSES_AND_LIBRARIES: EnumSet<Scope> =
EnumSet.of(Scope.ALL_CLASS_FILES, Scope.JAVA_LIBRARIES)
/** Scope-set used for detectors which are affected by Java libraries */
@JvmField
val JAVA_LIBRARY_SCOPE: EnumSet<Scope> = EnumSet.of(JAVA_LIBRARIES)
/** Scope-set used for detectors which are affected by a single binary resource file */
@JvmField
val BINARY_RESOURCE_FILE_SCOPE: EnumSet<Scope> = EnumSet.of(BINARY_RESOURCE_FILE)
/** No scopes */
@JvmField
val EMPTY: EnumSet<Scope> = EnumSet.noneOf(Scope::class.java)
}
}