blob: 076f5877e13adf357e8f427b0e21312c0fd3aaea [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 org.jetbrains.android.inspections.lint;
import com.android.annotations.NonNull;
import com.android.builder.model.*;
import com.android.sdklib.AndroidTargetHash;
import com.android.sdklib.AndroidVersion;
import com.android.tools.idea.gradle.IdeaAndroidProject;
import com.android.tools.idea.gradle.util.GradleUtil;
import com.android.tools.idea.model.AndroidModel;
import com.android.tools.idea.model.AndroidModuleInfo;
import com.android.tools.idea.model.ManifestInfo;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Project;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.graph.Graph;
import org.jetbrains.android.compiler.AndroidDexCompiler;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidRootUtil;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.util.AndroidCommonUtils;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.android.model.impl.JpsAndroidModuleProperties;
import java.io.File;
import java.util.*;
import static com.android.SdkConstants.APPCOMPAT_LIB_ARTIFACT;
import static com.android.SdkConstants.SUPPORT_LIB_ARTIFACT;
/**
* An {@linkplain IntellijLintProject} represents a lint project, which typically corresponds to a {@link Module},
* but can also correspond to a library "project" such as an {@link AndroidLibrary}.
*/
class IntellijLintProject extends Project {
/**
* Whether we support running .class file checks. No class file checks are currently registered as inspections.
* Since IntelliJ doesn't perform background compilation (e.g. only parsing, so there are no bytecode checks)
* this might need some work before we enable it.
*/
public static final boolean SUPPORT_CLASS_FILES = false;
protected AndroidVersion mMinSdkVersion;
protected AndroidVersion mTargetSdkVersion;
IntellijLintProject(@NonNull LintClient client,
@NonNull File dir,
@NonNull File referenceDir) {
super(client, dir, referenceDir);
}
/** Creates a set of projects for the given IntelliJ modules */
@NonNull
public static List<Project> create(@NonNull IntellijLintClient client, @Nullable List<VirtualFile> files, @NonNull Module... modules) {
List<Project> projects = Lists.newArrayList();
Map<Project,Module> projectMap = Maps.newHashMap();
Map<Module,Project> moduleMap = Maps.newHashMap();
Map<AndroidLibrary,Project> libraryMap = Maps.newHashMap();
if (files != null && !files.isEmpty()) {
// Wrap list with a mutable list since we'll be removing the files as we see them
files = Lists.newArrayList(files);
}
for (Module module : modules) {
addProjects(client, module, files, moduleMap, libraryMap, projectMap, projects);
}
client.setModuleMap(projectMap);
if (projects.size() > 1) {
// Partition the projects up such that we only return projects that aren't
// included by other projects (e.g. because they are library projects)
Set<Project> roots = new HashSet<Project>(projects);
for (Project project : projects) {
roots.removeAll(project.getAllLibraries());
}
return Lists.newArrayList(roots);
} else {
return projects;
}
}
/**
* Creates a project for a single file. Also optionally creates a main project for the file, if applicable.
*
* @param client the lint client
* @param file the file to create a project for
* @param module the module to create a project for
* @return a project for the file, as well as a project (or null) for the main Android module
*/
@NonNull
public static Pair<Project,Project> createForSingleFile(@NonNull IntellijLintClient client, @Nullable VirtualFile file, @NonNull Module module) {
// TODO: Can make this method even more lightweight: we don't need to initialize anything in the project (source paths etc)
// other than the metadata necessary for this file's type
LintModuleProject project = createModuleProject(client, module);
LintModuleProject main = null;
Map<Project,Module> projectMap = Maps.newHashMap();
if (project != null) {
project.setDirectLibraries(Collections.<Project>emptyList());
if (file != null) {
project.addFile(VfsUtilCore.virtualToIoFile(file));
}
projectMap.put(project, module);
// Supply a main project too, such that when you for example edit a file in a Java library,
// and lint asks for getMainProject().getMinSdk(), we return the min SDK of an application
// using the library, not "1" (the default for a module without a manifest)
if (!project.isAndroidProject()) {
Module androidModule = findAndroidModule(module);
if (androidModule != null) {
main = createModuleProject(client, androidModule);
if (main != null) {
projectMap.put(main, androidModule);
main.setDirectLibraries(Collections.<Project>singletonList(project));
}
}
}
}
client.setModuleMap(projectMap);
//noinspection ConstantConditions
return Pair.<Project,Project>create(project,main);
}
/** Find an Android module that depends on this module; prefer app modules over library modules */
@Nullable
private static Module findAndroidModule(@NonNull final Module module) {
// Search for dependencies of this module
Graph<Module> graph = ApplicationManager.getApplication().runReadAction(new Computable<Graph<Module>>() {
@Override
public Graph<Module> compute() {
return ModuleManager.getInstance(module.getProject()).moduleGraph();
}
});
Set<AndroidFacet> facets = Sets.newHashSet();
HashSet<Module> seen = Sets.newHashSet();
seen.add(module);
addAndroidModules(facets, seen, graph, module);
// Prefer Android app modules
for (AndroidFacet facet : facets) {
if (!facet.isLibraryProject()) {
return facet.getModule();
}
}
// Resort to library modules if no app module depends directly on it
if (!facets.isEmpty()) {
return facets.iterator().next().getModule();
}
return null;
}
private static void addAndroidModules(Set<AndroidFacet> androidFacets, Set<Module> seen, Graph<Module> graph, Module module) {
Iterator<Module> iterator = graph.getOut(module);
while (iterator.hasNext()) {
Module dep = iterator.next();
AndroidFacet facet = AndroidFacet.getInstance(dep);
if (facet != null) {
androidFacets.add(facet);
}
if (!seen.contains(dep)) {
seen.add(dep);
addAndroidModules(androidFacets, seen, graph, dep);
}
}
}
/**
* Recursively add lint projects for the given module, and any other module or library it depends on, and also
* populate the reverse maps so we can quickly map from a lint project to a corresponding module/library (used
* by the lint client
*/
private static void addProjects(@NonNull LintClient client,
@NonNull Module module,
@Nullable List<VirtualFile> files,
@NonNull Map<Module,Project> moduleMap,
@NonNull Map<AndroidLibrary, Project> libraryMap,
@NonNull Map<Project,Module> projectMap,
@NonNull List<Project> projects) {
if (moduleMap.containsKey(module)) {
return;
}
LintModuleProject project = createModuleProject(client, module);
if (project == null) {
// It's possible for the module to *depend* on Android code, e.g. in a Gradle
// project there will be a top-level non-Android module
List<AndroidFacet> dependentFacets = AndroidUtils.getAllAndroidDependencies(module, false);
for (AndroidFacet dependentFacet : dependentFacets) {
addProjects(client, dependentFacet.getModule(), files, moduleMap, libraryMap, projectMap, projects);
}
return;
}
projects.add(project);
moduleMap.put(module, project);
projectMap.put(project, module);
if (processFileFilter(module, files, project)) {
// No need to process dependencies when doing single file analysis
return;
}
List<Project> dependencies = Lists.newArrayList();
// No, this shouldn't use getAllAndroidDependencies; we may have non-Android dependencies that this won't include
// (e.g. Java-only modules)
List<AndroidFacet> dependentFacets = AndroidUtils.getAllAndroidDependencies(module, true);
for (AndroidFacet dependentFacet : dependentFacets) {
Project p = moduleMap.get(dependentFacet.getModule());
if (p != null) {
dependencies.add(p);
} else {
addProjects(client, dependentFacet.getModule(), files, moduleMap, libraryMap, projectMap, dependencies);
}
}
AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet != null && facet.requiresAndroidModel()) {
addGradleLibraryProjects(client, files, libraryMap, projects, facet, project, projectMap, dependencies);
}
project.setDirectLibraries(dependencies);
}
/**
* Checks whether we have a file filter (e.g. a set of specific files to check in the module rather than all files,
* and if so, and if all the files have been found, returns true)
*/
private static boolean processFileFilter(@NonNull Module module, @Nullable List<VirtualFile> files, @NonNull LintModuleProject project) {
if (files != null && !files.isEmpty()) {
ListIterator<VirtualFile> iterator = files.listIterator();
while (iterator.hasNext()) {
VirtualFile file = iterator.next();
if (module.getModuleContentScope().accept(file)) {
project.addFile(VfsUtilCore.virtualToIoFile(file));
iterator.remove();
}
}
if (files.isEmpty()) {
// We're only scanning a subset of files (typically the current file in the editor);
// in that case, don't initialize all the libraries etc
project.setDirectLibraries(Collections.<Project>emptyList());
return true;
}
}
return false;
}
/** Creates a new module project */
@Nullable
private static LintModuleProject createModuleProject(@NonNull LintClient client, @NonNull Module module) {
AndroidFacet facet = AndroidFacet.getInstance(module);
File dir;
if (facet != null) {
final VirtualFile mainContentRoot = AndroidRootUtil.getMainContentRoot(facet);
if (mainContentRoot == null) {
return null;
}
dir = new File(FileUtil.toSystemDependentName(mainContentRoot.getPath()));
} else {
String moduleDirPath = AndroidRootUtil.getModuleDirPath(module);
if (moduleDirPath == null) {
return null;
}
dir = new File(FileUtil.toSystemDependentName(moduleDirPath));
}
LintModuleProject project;
if (facet == null) {
project = new LintModuleProject(client, dir, dir, module);
AndroidFacet f = findAndroidFacetInProject(module.getProject());
if (f != null) {
project.mGradleProject = f.requiresAndroidModel();
}
}
else if (facet.requiresAndroidModel()) {
project = new LintGradleProject(client, dir, dir, facet);
}
else {
project = new LintAndroidProject(client, dir, dir, facet);
}
client.registerProject(dir, project);
return project;
}
public static boolean hasAndroidModule(@NonNull com.intellij.openapi.project.Project project) {
return findAndroidFacetInProject(project) != null;
}
@Nullable
private static AndroidFacet findAndroidFacetInProject(@NonNull com.intellij.openapi.project.Project project) {
ModuleManager moduleManager = ModuleManager.getInstance(project);
for (Module module : moduleManager.getModules()) {
AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet != null) {
return facet;
}
}
return null;
}
/** Adds any gradle library projects to the dependency list */
private static void addGradleLibraryProjects(@NonNull LintClient client,
@Nullable List<VirtualFile> files,
@NonNull Map<AndroidLibrary, Project> libraryMap,
@NonNull List<Project> projects,
@NonNull AndroidFacet facet,
@NonNull LintModuleProject project,
@NonNull Map<Project,Module> projectMap,
@NonNull List<Project> dependencies) {
AndroidModel androidModel = facet.getAndroidModel();
if (androidModel != null) {
Collection<AndroidLibrary> libraries = androidModel.getMainArtifact().getDependencies().getLibraries();
for (AndroidLibrary library : libraries) {
Project p = libraryMap.get(library);
if (p == null) {
File dir = library.getFolder();
p = new LintGradleLibraryProject(client, dir, dir, library);
libraryMap.put(library, p);
projectMap.put(p, facet.getModule());
projects.add(p);
if (files != null) {
VirtualFile libraryDir = LocalFileSystem.getInstance().findFileByIoFile(dir);
if (libraryDir != null) {
ListIterator<VirtualFile> iterator = files.listIterator();
while (iterator.hasNext()) {
VirtualFile file = iterator.next();
if (VfsUtilCore.isAncestor(libraryDir, file, false)) {
project.addFile(VfsUtilCore.virtualToIoFile(file));
iterator.remove();
}
}
}
if (files.isEmpty()) {
files = null; // No more work in other modules
}
}
}
dependencies.add(p);
}
}
}
@Override
protected void initialize() {
// NOT calling super: super performs ADT/ant initialization. Here we want to use
// the gradle data instead
}
protected static boolean depsDependsOn(@NonNull Project project, @NonNull String artifact) {
// Checks project dependencies only; used when there is no model
for (Project dependency : project.getDirectLibraries()) {
Boolean b = dependency.dependsOn(artifact);
if (b != null && b) {
return true;
}
}
return false;
}
private static class LintModuleProject extends IntellijLintProject {
private Module myModule;
public void setDirectLibraries(List<Project> libraries) {
mDirectLibraries = libraries;
}
private LintModuleProject(@NonNull LintClient client, @NonNull File dir, @NonNull File referenceDir, Module module) {
super(client, dir, referenceDir);
myModule = module;
}
@Override
public boolean isAndroidProject() {
return false;
}
@NonNull
@Override
public List<File> getJavaSourceFolders() {
if (mJavaSourceFolders == null) {
VirtualFile[] sourceRoots = ModuleRootManager.getInstance(myModule).getSourceRoots(false);
List<File> dirs = new ArrayList<File>(sourceRoots.length);
for (VirtualFile root : sourceRoots) {
dirs.add(new File(root.getPath()));
}
mJavaSourceFolders = dirs;
}
return mJavaSourceFolders;
}
@NonNull
@Override
public List<File> getTestSourceFolders() {
if (mTestSourceFolders == null) {
ModuleRootManager manager = ModuleRootManager.getInstance(myModule);
VirtualFile[] sourceRoots = manager.getSourceRoots(false);
VirtualFile[] sourceAndTestRoots = manager.getSourceRoots(true);
List<File> dirs = new ArrayList<File>(sourceAndTestRoots.length);
for (VirtualFile root : sourceAndTestRoots) {
if (!ArrayUtil.contains(root, sourceRoots)) {
dirs.add(new File(root.getPath()));
}
}
mTestSourceFolders = dirs;
}
return mTestSourceFolders;
}
@NonNull
@Override
public List<File> getJavaClassFolders() {
if (SUPPORT_CLASS_FILES) {
if (mJavaClassFolders == null) {
VirtualFile folder = AndroidDexCompiler.getOutputDirectoryForDex(myModule);
if (folder != null) {
mJavaClassFolders = Collections.singletonList(VfsUtilCore.virtualToIoFile(folder));
} else {
mJavaClassFolders = Collections.emptyList();
}
}
return mJavaClassFolders;
}
return Collections.emptyList();
}
@NonNull
@Override
public List<File> getJavaLibraries() {
if (SUPPORT_CLASS_FILES) {
if (mJavaLibraries == null) {
mJavaLibraries = Lists.newArrayList();
final OrderEntry[] entries = ModuleRootManager.getInstance(myModule).getOrderEntries();
// loop in the inverse order to resolve dependencies on the libraries, so that if a library
// is required by two higher level libraries it can be inserted in the correct place
for (int i = entries.length - 1; i >= 0; i--) {
final OrderEntry orderEntry = entries[i];
if (orderEntry instanceof LibraryOrderEntry) {
LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)orderEntry;
VirtualFile[] classes = libraryOrderEntry.getRootFiles(OrderRootType.CLASSES);
if (classes != null) {
for (VirtualFile file : classes) {
mJavaLibraries.add(VfsUtilCore.virtualToIoFile(file));
}
}
}
}
}
return mJavaLibraries;
}
return Collections.emptyList();
}
}
/** Wraps an Android module */
private static class LintAndroidProject extends LintModuleProject {
protected final AndroidFacet myFacet;
private LintAndroidProject(@NonNull LintClient client, @NonNull File dir, @NonNull File referenceDir, @NonNull AndroidFacet facet) {
super(client, dir, referenceDir, facet.getModule());
myFacet = facet;
mGradleProject = false;
mLibrary = myFacet.isLibraryProject();
AndroidPlatform platform = AndroidPlatform.getInstance(myFacet.getModule());
if (platform != null) {
mBuildSdk = platform.getApiLevel();
}
}
@Override
public boolean isAndroidProject() {
return true;
}
@NonNull
@Override
public String getName() {
return myFacet.getModule().getName();
}
@Override
@NonNull
public List<File> getManifestFiles() {
if (mManifestFiles == null) {
VirtualFile manifestFile = AndroidRootUtil.getPrimaryManifestFile(myFacet);
if (manifestFile != null) {
mManifestFiles = Collections.singletonList(VfsUtilCore.virtualToIoFile(manifestFile));
} else {
mManifestFiles = Collections.emptyList();
}
}
return mManifestFiles;
}
@NonNull
@Override
public List<File> getProguardFiles() {
if (mProguardFiles == null) {
assert !myFacet.requiresAndroidModel(); // Should be overridden to read from gradle state
final JpsAndroidModuleProperties properties = myFacet.getProperties();
if (properties.RUN_PROGUARD) {
final List<String> urls = properties.myProGuardCfgFiles;
if (!urls.isEmpty()) {
mProguardFiles = new ArrayList<File>();
for (String osPath : AndroidUtils.urlsToOsPaths(urls, null)) {
if (!osPath.contains(AndroidCommonUtils.SDK_HOME_MACRO)) {
mProguardFiles.add(new File(osPath));
}
}
}
}
if (mProguardFiles == null) {
mProguardFiles = Collections.emptyList();
}
}
return mProguardFiles;
}
@NonNull
@Override
public List<File> getResourceFolders() {
if (mResourceFolders == null) {
List<VirtualFile> folders = myFacet.getResourceFolderManager().getFolders();
List<File> dirs = Lists.newArrayListWithExpectedSize(folders.size());
for (VirtualFile folder : folders) {
dirs.add(VfsUtilCore.virtualToIoFile(folder));
}
mResourceFolders = dirs;
}
return mResourceFolders;
}
@Nullable
@Override
public Boolean dependsOn(@NonNull String artifact) {
if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
if (mSupportLib == null) {
final OrderEntry[] entries = ModuleRootManager.getInstance(myFacet.getModule()).getOrderEntries();
libraries:
for (int i = entries.length - 1; i >= 0; i--) {
final OrderEntry orderEntry = entries[i];
if (orderEntry instanceof LibraryOrderEntry) {
LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)orderEntry;
VirtualFile[] classes = libraryOrderEntry.getRootFiles(OrderRootType.CLASSES);
if (classes != null) {
for (VirtualFile file : classes) {
if (file.getName().equals("android-support-v4.jar")) {
mSupportLib = true;
break libraries;
}
}
}
}
}
if (mSupportLib == null) {
mSupportLib = depsDependsOn(this, artifact);
}
}
return mSupportLib;
} else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
if (mAppCompat == null) {
mAppCompat = false;
final OrderEntry[] entries = ModuleRootManager.getInstance(myFacet.getModule()).getOrderEntries();
for (int i = entries.length - 1; i >= 0; i--) {
final OrderEntry orderEntry = entries[i];
if (orderEntry instanceof ModuleOrderEntry) {
ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)orderEntry;
Module module = moduleOrderEntry.getModule();
if (module == null || module == myFacet.getModule()) {
continue;
}
AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet == null) {
continue;
}
ManifestInfo manifestInfo = ManifestInfo.get(module, false);
if ("android.support.v7.appcompat".equals(manifestInfo.getPackage())) {
mAppCompat = true;
break;
}
}
}
}
return mAppCompat;
} else {
return super.dependsOn(artifact);
}
}
}
private static class LintGradleProject extends LintAndroidProject {
/**
* Creates a new Project. Use one of the factory methods to create.
*/
private LintGradleProject(@NonNull LintClient client, @NonNull File dir, @NonNull File referenceDir, @NonNull AndroidFacet facet) {
super(client, dir, referenceDir, facet);
mGradleProject = true;
mMergeManifests = true;
}
@NonNull
@Override
public List<File> getManifestFiles() {
if (mManifestFiles == null) {
mManifestFiles = Lists.newArrayList();
File mainManifest = myFacet.getMainSourceProvider().getManifestFile();
if (mainManifest.exists()) {
mManifestFiles.add(mainManifest);
}
List<SourceProvider> flavorSourceProviders = myFacet.getFlavorSourceProviders();
if (flavorSourceProviders != null) {
for (SourceProvider provider : flavorSourceProviders) {
File manifestFile = provider.getManifestFile();
if (manifestFile.exists()) {
mManifestFiles.add(manifestFile);
}
}
}
SourceProvider multiProvider = myFacet.getMultiFlavorSourceProvider();
if (multiProvider != null) {
File manifestFile = multiProvider.getManifestFile();
if (manifestFile.exists()) {
mManifestFiles.add(manifestFile);
}
}
SourceProvider buildTypeSourceProvider = myFacet.getBuildTypeSourceProvider();
if (buildTypeSourceProvider != null) {
File manifestFile = buildTypeSourceProvider.getManifestFile();
if (manifestFile.exists()) {
mManifestFiles.add(manifestFile);
}
}
SourceProvider variantProvider = myFacet.getVariantSourceProvider();
if (variantProvider != null) {
File manifestFile = variantProvider.getManifestFile();
if (manifestFile.exists()) {
mManifestFiles.add(manifestFile);
}
}
}
return mManifestFiles;
}
@NonNull
@Override
public List<File> getProguardFiles() {
if (mProguardFiles == null) {
if (myFacet.requiresAndroidModel()) {
IdeaAndroidProject androidModel = myFacet.getAndroidModel();
if (androidModel != null) {
ProductFlavor flavor = androidModel.getAndroidProject().getDefaultConfig().getProductFlavor();
mProguardFiles = Lists.newArrayList();
for (File file : flavor.getProguardFiles()) {
if (file.exists()) {
mProguardFiles.add(file);
}
}
try {
for (File file : flavor.getConsumerProguardFiles()) {
if (file.exists()) {
mProguardFiles.add(file);
}
}
} catch (Throwable t) {
// On some models, this threw
// org.gradle.tooling.model.UnsupportedMethodException: Unsupported method: BaseConfig.getConsumerProguardFiles().
// Playing it safe for a while.
}
}
}
if (mProguardFiles == null) {
mProguardFiles = Collections.emptyList();
}
}
return mProguardFiles;
}
@NonNull
@Override
public List<File> getJavaClassFolders() {
if (SUPPORT_CLASS_FILES) {
if (mJavaClassFolders == null) {
// Overridden because we don't synchronize the gradle output directory to
// the AndroidDexCompiler settings the way java source roots are mapped into
// the module content root settings
File dir = null;
if (myFacet.requiresAndroidModel()) {
AndroidModel androidModel = myFacet.getAndroidModel();
if (androidModel != null) {
dir = androidModel.getMainArtifact().getClassesFolder();
}
}
if (dir != null) {
mJavaClassFolders = Collections.singletonList(dir);
} else {
mJavaClassFolders = Collections.emptyList();
}
}
return mJavaClassFolders;
}
return Collections.emptyList();
}
@NonNull
@Override
public List<File> getJavaLibraries() {
if (SUPPORT_CLASS_FILES) {
if (mJavaLibraries == null) {
if (myFacet.requiresAndroidModel() && myFacet.getAndroidModel() != null) {
AndroidModel androidModel = myFacet.getAndroidModel();
Collection<JavaLibrary> libs = androidModel.getMainArtifact().getDependencies().getJavaLibraries();
mJavaLibraries = Lists.newArrayListWithExpectedSize(libs.size());
for (JavaLibrary lib : libs) {
File jar = lib.getJarFile();
if (jar.exists()) {
mJavaLibraries.add(jar);
}
}
} else {
mJavaLibraries = super.getJavaLibraries();
}
}
return mJavaLibraries;
}
return Collections.emptyList();
}
@Nullable
@Override
public String getPackage() {
String manifestPackage = super.getPackage();
// For now, lint only needs the manifest package; not the potentially variant specific
// package. As part of the Gradle work on the Lint API we should make two separate
// package lookup methods -- one for the manifest package, one for the build package
if (manifestPackage != null) {
return manifestPackage;
}
IdeaAndroidProject androidModel = myFacet.getAndroidModel();
if (androidModel != null) {
return androidModel.getApplicationId();
}
return null;
}
@NonNull
@Override
public AndroidVersion getMinSdkVersion() {
if (mMinSdkVersion == null) {
mMinSdkVersion = AndroidModuleInfo.get(myFacet).getMinSdkVersion();
}
return mMinSdkVersion;
}
@NonNull
@Override
public AndroidVersion getTargetSdkVersion() {
if (mTargetSdkVersion == null) {
mTargetSdkVersion = AndroidModuleInfo.get(myFacet).getTargetSdkVersion();
}
return mTargetSdkVersion;
}
@Override
public int getBuildSdk() {
IdeaAndroidProject androidModel = myFacet.getAndroidModel();
if (androidModel != null) {
String compileTarget = androidModel.getAndroidProject().getCompileTarget();
AndroidVersion version = AndroidTargetHash.getPlatformVersion(compileTarget);
if (version != null) {
return version.getApiLevel();
}
}
AndroidPlatform platform = AndroidPlatform.getInstance(myFacet.getModule());
if (platform != null) {
return platform.getApiLevel();
}
return super.getBuildSdk();
}
@Nullable
@Override
public AndroidProject getGradleProjectModel() {
IdeaAndroidProject androidModel = myFacet.getAndroidModel();
if (androidModel != null) {
androidModel.getSelectedVariant();
return androidModel.getAndroidProject();
}
return null;
}
@Nullable
@Override
public Variant getCurrentVariant() {
IdeaAndroidProject androidModel = myFacet.getAndroidModel();
if (androidModel != null) {
return androidModel.getSelectedVariant();
}
return null;
}
@Nullable
@Override
public AndroidLibrary getGradleLibraryModel() {
return null;
}
@Nullable
@Override
public Boolean dependsOn(@NonNull String artifact) {
if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
if (mSupportLib == null) {
if (myFacet.requiresAndroidModel() && myFacet.getAndroidModel() != null) {
mSupportLib = GradleUtil.dependsOn(myFacet.getAndroidModel(), artifact);
} else {
mSupportLib = depsDependsOn(this, artifact);
}
}
return mSupportLib;
} else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
if (mAppCompat == null) {
if (myFacet.requiresAndroidModel() && myFacet.getAndroidModel() != null) {
mAppCompat = GradleUtil.dependsOn(myFacet.getAndroidModel(), artifact);
} else {
mAppCompat = depsDependsOn(this, artifact);
}
}
return mAppCompat;
} else {
// Some other (not yet directly cached result)
if (myFacet.requiresAndroidModel() && myFacet.getAndroidModel() != null
&& GradleUtil.dependsOn(myFacet.getAndroidModel(), artifact)) {
return true;
}
return super.dependsOn(artifact);
}
}
}
private static class LintGradleLibraryProject extends IntellijLintProject {
private final AndroidLibrary myLibrary;
private LintGradleLibraryProject(@NonNull LintClient client,
@NonNull File dir,
@NonNull File referenceDir,
@NonNull AndroidLibrary library) {
super(client, dir, referenceDir);
myLibrary = library;
mLibrary = true;
mMergeManifests = true;
mReportIssues = false;
mGradleProject = true;
mDirectLibraries = Collections.emptyList();
}
@NonNull
@Override
public List<File> getManifestFiles() {
if (mManifestFiles == null) {
File manifest = myLibrary.getManifest();
if (manifest.exists()) {
mManifestFiles = Collections.singletonList(manifest);
} else {
mManifestFiles = Collections.emptyList();
}
}
return mManifestFiles;
}
@NonNull
@Override
public List<File> getProguardFiles() {
if (mProguardFiles == null) {
File proguardRules = myLibrary.getProguardRules();
if (proguardRules.exists()) {
mProguardFiles = Collections.singletonList(proguardRules);
} else {
mProguardFiles = Collections.emptyList();
}
}
return mProguardFiles;
}
@NonNull
@Override
public List<File> getResourceFolders() {
if (mResourceFolders == null) {
File folder = myLibrary.getResFolder();
if (folder.exists()) {
mResourceFolders = Collections.singletonList(folder);
} else {
mResourceFolders = Collections.emptyList();
}
}
return mResourceFolders;
}
@NonNull
@Override
public List<File> getJavaSourceFolders() {
return Collections.emptyList();
}
@NonNull
@Override
public List<File> getJavaClassFolders() {
return Collections.emptyList();
}
@NonNull
@Override
public List<File> getJavaLibraries() {
if (SUPPORT_CLASS_FILES) {
if (mJavaLibraries == null) {
mJavaLibraries = Lists.newArrayList();
File jarFile = myLibrary.getJarFile();
if (jarFile.exists()) {
mJavaLibraries.add(jarFile);
}
for (File local : myLibrary.getLocalJars()) {
if (local.exists()) {
mJavaLibraries.add(local);
}
}
}
return mJavaLibraries;
}
return Collections.emptyList();
}
@Nullable
@Override
public AndroidProject getGradleProjectModel() {
return null;
}
@Nullable
@Override
public AndroidLibrary getGradleLibraryModel() {
return myLibrary;
}
@Nullable
@Override
public Boolean dependsOn(@NonNull String artifact) {
if (SUPPORT_LIB_ARTIFACT.equals(artifact)) {
if (mSupportLib == null) {
mSupportLib = GradleUtil.dependsOn(myLibrary, artifact, true);
}
return mSupportLib;
} else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) {
if (mAppCompat == null) {
mAppCompat = GradleUtil.dependsOn(myLibrary, artifact, true);
}
return mAppCompat;
} else {
// Some other (not yet directly cached result)
if (GradleUtil.dependsOn(myLibrary, artifact, true)) {
return true;
}
return super.dependsOn(artifact);
}
}
}
}