/* * 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.klint.detector.api; import static com.android.SdkConstants.ANDROID_MANIFEST_XML; import static com.android.SdkConstants.DOT_CLASS; import static com.android.SdkConstants.DOT_GRADLE; import static com.android.SdkConstants.DOT_JAVA; import static com.android.SdkConstants.DOT_PNG; import static com.android.SdkConstants.DOT_PROPERTIES; import static com.android.SdkConstants.DOT_XML; import static com.android.SdkConstants.FN_PROJECT_PROGUARD_FILE; import static com.android.SdkConstants.OLD_PROGUARD_FILE; import static com.android.SdkConstants.RES_FOLDER; import com.android.annotations.NonNull; import com.google.common.annotations.Beta; import java.io.File; import java.util.Collection; import java.util.EnumSet; import java.util.List; /** * 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. * <p> * <b>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.</b> */ @Beta public enum Scope { /** * The analysis only considers a single XML resource file at a time. * <p> * 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. * <p> * 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 <b>all</b> the resource file. This scope must not * be used in conjunction with {@link #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 source file at a time. * <p> * Issues which are only affected by a single Java source file can be * checked for incrementally when a Java source file is edited. */ JAVA_FILE, /** * The analysis considers <b>all</b> the Java source files together. * <p> * This flag is mutually exclusive with {@link #JAVA_FILE}. */ ALL_JAVA_FILES, /** * The analysis only considers a single Java class file at a time. * <p> * Issues which are only affected by a single Java class file can be checked * for incrementally when a Java source file is edited and then recompiled. */ CLASS_FILE, /** * The analysis considers <b>all</b> the Java class files together. * <p> * This flag is mutually exclusive with {@link #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 {@link Detector#run(Context)}} on the detectors unconditionally. */ OTHER; /** * 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 */ public static boolean checkSingleFile(@NonNull EnumSet<Scope> scopes) { int size = scopes.size(); if (size == 2) { // When single checking a Java source file, we check both its Java source // and the associated class files return scopes.contains(JAVA_FILE) && scopes.contains(CLASS_FILE); } else { return size == 1 && (scopes.contains(JAVA_FILE) || scopes.contains(CLASS_FILE) || scopes.contains(RESOURCE_FILE) || scopes.contains(PROGUARD_FILE) || scopes.contains(PROPERTY_FILE) || scopes.contains(GRADLE_FILE) || scopes.contains(MANIFEST)); } } /** * Returns the intersection of two scope sets * * @param scope1 the first set to intersect * @param scope2 the second set to intersect * @return the intersection of the two sets */ @NonNull public static EnumSet<Scope> intersect( @NonNull EnumSet<Scope> scope1, @NonNull EnumSet<Scope> scope2) { EnumSet<Scope> 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 */ @NonNull public static EnumSet<Scope> infer(@NonNull Collection<Project> projects) { // Infer the scope EnumSet<Scope> scope = EnumSet.noneOf(Scope.class); for (Project project : projects) { List<File> subset = project.getSubset(); if (subset != null) { for (File file : subset) { String name = file.getName(); if (name.equals(ANDROID_MANIFEST_XML)) { scope.add(MANIFEST); } else if (name.endsWith(DOT_XML)) { scope.add(RESOURCE_FILE); } else if (name.endsWith(".kt")) { scope.add(JAVA_FILE); } else if (name.endsWith(DOT_CLASS)) { scope.add(CLASS_FILE); } else if (name.endsWith(DOT_GRADLE)) { scope.add(GRADLE_FILE); } else if (name.equals(OLD_PROGUARD_FILE) || name.equals(FN_PROJECT_PROGUARD_FILE)) { scope.add(PROGUARD_FILE); } else if (name.endsWith(DOT_PROPERTIES)) { scope.add(PROPERTY_FILE); } else if (name.endsWith(DOT_PNG)) { scope.add(BINARY_RESOURCE_FILE); } else if (name.equals(RES_FOLDER) || file.getParent().equals(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 */ public static final EnumSet<Scope> ALL = EnumSet.allOf(Scope.class); /** Scope-set used for detectors which are affected by a single resource file */ public static final EnumSet<Scope> RESOURCE_FILE_SCOPE = EnumSet.of(RESOURCE_FILE); /** Scope-set used for detectors which are affected by a single resource folder */ public static final EnumSet<Scope> RESOURCE_FOLDER_SCOPE = EnumSet.of(RESOURCE_FOLDER); /** Scope-set used for detectors which scan all resources */ public static final EnumSet<Scope> ALL_RESOURCES_SCOPE = EnumSet.of(ALL_RESOURCE_FILES); /** Scope-set used for detectors which are affected by a single Java source file */ public static final EnumSet<Scope> JAVA_FILE_SCOPE = EnumSet.of(JAVA_FILE); /** Scope-set used for detectors which are affected by a single Java class file */ public static final EnumSet<Scope> CLASS_FILE_SCOPE = EnumSet.of(CLASS_FILE); /** Scope-set used for detectors which are affected by a single Gradle build file */ public static final EnumSet<Scope> GRADLE_SCOPE = EnumSet.of(GRADLE_FILE); /** Scope-set used for detectors which are affected by the manifest only */ public static final EnumSet<Scope> MANIFEST_SCOPE = EnumSet.of(MANIFEST); /** Scope-set used for detectors which correspond to some other context */ public static final EnumSet<Scope> OTHER_SCOPE = EnumSet.of(OTHER); /** Scope-set used for detectors which are affected by a single ProGuard class file */ public static final EnumSet<Scope> PROGUARD_SCOPE = EnumSet.of(PROGUARD_FILE); /** Scope-set used for detectors which correspond to property files */ public static final EnumSet<Scope> PROPERTY_SCOPE = EnumSet.of(PROPERTY_FILE); /** Resource XML files and manifest files */ public static final EnumSet<Scope> MANIFEST_AND_RESOURCE_SCOPE = EnumSet.of(Scope.MANIFEST, Scope.RESOURCE_FILE); /** Scope-set used for detectors which are affected by single XML and Java source files */ public static final EnumSet<Scope> JAVA_AND_RESOURCE_FILES = EnumSet.of(RESOURCE_FILE, JAVA_FILE); /** Scope-set used for analyzing individual class files and all resource files */ public static final EnumSet<Scope> CLASS_AND_ALL_RESOURCE_FILES = EnumSet.of(ALL_RESOURCE_FILES, CLASS_FILE); /** Scope-set used for analyzing all class files, including those in libraries */ public static final EnumSet<Scope> ALL_CLASSES_AND_LIBRARIES = EnumSet.of(Scope.ALL_CLASS_FILES, Scope.JAVA_LIBRARIES); /** Scope-set used for detectors which are affected by Java libraries */ public static final EnumSet<Scope> JAVA_LIBRARY_SCOPE = EnumSet.of(JAVA_LIBRARIES); /** Scope-set used for detectors which are affected by a single binary resource file */ public static final EnumSet<Scope> BINARY_RESOURCE_FILE_SCOPE = EnumSet.of(BINARY_RESOURCE_FILE); }