From 991e783ccfde43432f4768fe55cc8368aea9faa9 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 1 Apr 2016 16:44:26 +0300 Subject: [PATCH] Lint: Make lint run normally on both older and newer versions of IDEA (IdeaAndroidProject was replaced by AndroidGradleModel) --- plugins/lint/lint-checks/lint-checks.iml | 1 + plugins/lint/lint-idea/lint-idea.iml | 1 + .../inspections/klint/AndroidModelFacade.kt | 120 ++++++++++++++++++ .../inspections/klint/IntellijLintClient.java | 4 +- .../klint/IntellijLintProject.java | 95 +++++++------- .../inspections/klint/IntellijLintUtils.java | 32 ++++- 6 files changed, 197 insertions(+), 56 deletions(-) create mode 100644 plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidModelFacade.kt diff --git a/plugins/lint/lint-checks/lint-checks.iml b/plugins/lint/lint-checks/lint-checks.iml index 4348c78f8f4..84af3755987 100644 --- a/plugins/lint/lint-checks/lint-checks.iml +++ b/plugins/lint/lint-checks/lint-checks.iml @@ -9,6 +9,7 @@ + diff --git a/plugins/lint/lint-idea/lint-idea.iml b/plugins/lint/lint-idea/lint-idea.iml index 9041e5bc8e8..93dc751f6a3 100644 --- a/plugins/lint/lint-idea/lint-idea.iml +++ b/plugins/lint/lint-idea/lint-idea.iml @@ -10,6 +10,7 @@ + diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidModelFacade.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidModelFacade.kt new file mode 100644 index 00000000000..7bf38c07e49 --- /dev/null +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidModelFacade.kt @@ -0,0 +1,120 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * 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. + */ + +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * 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.klint + +import com.android.builder.model.AndroidProject +import com.android.builder.model.SourceProvider +import com.android.builder.model.Variant +import com.android.tools.idea.gradle.util.GradleUtil +import org.jetbrains.android.facet.AndroidFacet +import kotlin.reflect.memberFunctions +import kotlin.reflect.staticFunctions + +class AndroidModelFacade(val facet: AndroidFacet) { + private val model by lazy { loadModel() } + private val modelClass by lazy { + try { + Class.forName("com.android.tools.idea.gradle.AndroidGradleModel") + } + catch(e: ClassNotFoundException) { + null + } + } + + fun isModelReady() = model != null + + @Suppress("UNCHECKED_CAST") + fun getFlavorSourceProviders(): List? + = getFacetOrModel("getFlavorSourceProviders") as? List + + fun getMultiFlavorSourceProvider(): SourceProvider? + = getFacetOrModel("getMultiFlavorSourceProvider") as? SourceProvider + + fun getBuildTypeSourceProvider(): SourceProvider? + = getFacetOrModel("getBuildTypeSourceProvider") as? SourceProvider + + fun getVariantSourceProvider(): SourceProvider? + = getFacetOrModel("getVariantSourceProvider") as? SourceProvider + + private fun getFacetOrModel(methodName: String): Any? { + if (modelClass == null) { + return facet.javaClass.kotlin.memberFunctions + .firstOrNull { it.name == methodName } + ?.call(facet) + } + + return modelClass?.kotlin?.memberFunctions + ?.firstOrNull { it.name == methodName } + ?.call(model) + } + + fun getDependsOn(artifact: String): Boolean { + val model = model ?: return false + return GradleUtil::class.staticFunctions.firstOrNull { + val type = it.parameters[0].type.toString() + it.parameters.size == 2 && "IdeaAndroidProject" in type || "AndroidGradleModel" in type + }?.call(model, artifact) as? Boolean ?: false + } + + fun getAndroidProject(): AndroidProject? { + val model = model ?: return null + return modelClass?.kotlin?.memberFunctions + ?.firstOrNull { it.name == "getDelegate" || it.name == "getAndroidProject" } + ?.call(model) as? AndroidProject + } + + fun getSelectedVariant(): Variant? { + val model = model ?: return null + return modelClass?.kotlin?.memberFunctions + ?.firstOrNull { it.name == "getSelectedVariant" } + ?.call(model) as? Variant + } + + private fun loadModel(): Any? { + try { + val getAndroidProjectInfoFun = AndroidFacet::class.memberFunctions.singleOrNull { + it.name == "getIdeaAndroidProject" || it.name == "getAndroidModel" + } + + return getAndroidProjectInfoFun?.call(facet) + } + catch(e: Throwable) { + return false + } + } + + fun computePackageName(): String? { + return getSelectedVariant()?.mainArtifact?.applicationId + } + +} \ No newline at end of file diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java index 235e06a454e..c211eb38b80 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java @@ -382,9 +382,9 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC Module module = getModule(); if (module != null) { AndroidFacet facet = AndroidFacet.getInstance(module); - return facet != null && facet.isGradleProject(); + return facet != null && IntellijLintUtils.isGradleModule(facet); } - return Projects.isGradleProject(myProject); + return false; } @Override diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java index a724c0773ad..4c1281b350e 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintProject.java @@ -19,7 +19,6 @@ 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.AndroidModuleInfo; import com.android.tools.idea.model.ManifestInfo; @@ -54,6 +53,7 @@ import java.util.*; import static com.android.SdkConstants.APPCOMPAT_LIB_ARTIFACT; import static com.android.SdkConstants.SUPPORT_LIB_ARTIFACT; +import static org.jetbrains.android.inspections.klint.IntellijLintUtils.*; /** * An {@linkplain IntellijLintProject} represents a lint project, which typically corresponds to a {@link Module}, @@ -245,7 +245,7 @@ class IntellijLintProject extends Project { } AndroidFacet facet = AndroidFacet.getInstance(module); - if (facet != null && facet.isGradleProject()) { + if (facet != null && isGradleModule(facet)) { addGradleLibraryProjects(client, files, libraryMap, projects, facet, project, projectMap, dependencies); } @@ -301,10 +301,10 @@ class IntellijLintProject extends Project { project = new LintModuleProject(client, dir, dir, module); AndroidFacet f = findAndroidFacetInProject(module.getProject()); if (f != null) { - project.mGradleProject = f.isGradleProject(); + project.mGradleProject = isGradleModule(f); } } - else if (facet.isGradleProject()) { + else if (isGradleModule((facet))) { project = new LintGradleProject(client, dir, dir, facet); } else { @@ -340,9 +340,11 @@ class IntellijLintProject extends Project { @NonNull LintModuleProject project, @NonNull Map projectMap, @NonNull List dependencies) { - File dir;IdeaAndroidProject gradleProject = facet.getIdeaAndroidProject(); - if (gradleProject != null) { - Collection libraries = gradleProject.getSelectedVariant().getMainArtifact().getDependencies().getLibraries(); + File dir; + AndroidModelFacade androidModelFacade = getModelFacade(facet); + Variant selectedVariant = androidModelFacade.getSelectedVariant(); + if (selectedVariant != null) { + Collection libraries = selectedVariant.getMainArtifact().getDependencies().getLibraries(); for (AndroidLibrary library : libraries) { Project p = libraryMap.get(library); if (p == null) { @@ -540,7 +542,7 @@ class IntellijLintProject extends Project { @Override public List getProguardFiles() { if (mProguardFiles == null) { - assert !myFacet.isGradleProject(); // Should be overridden to read from gradle state + assert !isGradleModule(myFacet); // Should be overridden to read from gradle state final JpsAndroidModuleProperties properties = myFacet.getProperties(); if (properties.RUN_PROGUARD) { @@ -659,7 +661,7 @@ class IntellijLintProject extends Project { mManifestFiles.add(mainManifest); } - List flavorSourceProviders = myFacet.getFlavorSourceProviders(); + List flavorSourceProviders = getModelFacade(myFacet).getFlavorSourceProviders(); if (flavorSourceProviders != null) { for (SourceProvider provider : flavorSourceProviders) { File manifestFile = provider.getManifestFile(); @@ -669,7 +671,7 @@ class IntellijLintProject extends Project { } } - SourceProvider multiProvider = myFacet.getMultiFlavorSourceProvider(); + SourceProvider multiProvider = getModelFacade(myFacet).getMultiFlavorSourceProvider(); if (multiProvider != null) { File manifestFile = multiProvider.getManifestFile(); if (manifestFile.exists()) { @@ -677,7 +679,7 @@ class IntellijLintProject extends Project { } } - SourceProvider buildTypeSourceProvider = myFacet.getBuildTypeSourceProvider(); + SourceProvider buildTypeSourceProvider = getModelFacade(myFacet).getBuildTypeSourceProvider(); if (buildTypeSourceProvider != null) { File manifestFile = buildTypeSourceProvider.getManifestFile(); if (manifestFile.exists()) { @@ -685,7 +687,7 @@ class IntellijLintProject extends Project { } } - SourceProvider variantProvider = myFacet.getVariantSourceProvider(); + SourceProvider variantProvider = getModelFacade(myFacet).getVariantSourceProvider(); if (variantProvider != null) { File manifestFile = variantProvider.getManifestFile(); if (manifestFile.exists()) { @@ -701,10 +703,11 @@ class IntellijLintProject extends Project { @Override public List getProguardFiles() { if (mProguardFiles == null) { - if (myFacet.isGradleProject()) { - IdeaAndroidProject gradleProject = myFacet.getIdeaAndroidProject(); - if (gradleProject != null) { - ProductFlavor flavor = gradleProject.getDelegate().getDefaultConfig().getProductFlavor(); + if (isGradleModule(myFacet)) { + AndroidModelFacade androidModelFacade = getModelFacade(myFacet); + AndroidProject androidProject = androidModelFacade.getAndroidProject(); + if (androidProject != null) { + ProductFlavor flavor = androidProject.getDefaultConfig().getProductFlavor(); mProguardFiles = Lists.newArrayList(); for (File file : flavor.getProguardFiles()) { if (file.exists()) { @@ -742,10 +745,10 @@ class IntellijLintProject extends Project { // the AndroidDexCompiler settings the way java source roots are mapped into // the module content root settings File dir = null; - if (myFacet.isGradleProject()) { - IdeaAndroidProject gradleProject = myFacet.getIdeaAndroidProject(); - if (gradleProject != null) { - Variant variant = gradleProject.getSelectedVariant(); + if (isGradleModule(myFacet)) { + AndroidModelFacade androidModelFacade = getModelFacade(myFacet); + Variant variant = androidModelFacade.getSelectedVariant(); + if (variant != null) { dir = variant.getMainArtifact().getClassesFolder(); } } @@ -767,9 +770,10 @@ class IntellijLintProject extends Project { public List getJavaLibraries() { if (SUPPORT_CLASS_FILES) { if (mJavaLibraries == null) { - if (myFacet.isGradleProject() && myFacet.getIdeaAndroidProject() != null) { - IdeaAndroidProject gradleProject = myFacet.getIdeaAndroidProject(); - Collection libs = gradleProject.getSelectedVariant().getMainArtifact().getDependencies().getJavaLibraries(); + AndroidModelFacade androidModelFacade = getModelFacade(myFacet); + Variant selectedVariant = androidModelFacade.getSelectedVariant(); + if (isGradleModule(myFacet) && selectedVariant != null) { + Collection libs = selectedVariant.getMainArtifact().getDependencies().getJavaLibraries(); mJavaLibraries = Lists.newArrayListWithExpectedSize(libs.size()); for (JavaLibrary lib : libs) { File jar = lib.getJarFile(); @@ -798,12 +802,7 @@ class IntellijLintProject extends Project { return manifestPackage; } - IdeaAndroidProject project = myFacet.getIdeaAndroidProject(); - if (project != null) { - return project.computePackageName(); - } - - return null; + return getModelFacade(myFacet).computePackageName(); } @NonNull @@ -828,9 +827,10 @@ class IntellijLintProject extends Project { @Override public int getBuildSdk() { - IdeaAndroidProject ideaAndroidProject = myFacet.getIdeaAndroidProject(); - if (ideaAndroidProject != null) { - String compileTarget = ideaAndroidProject.getDelegate().getCompileTarget(); + AndroidModelFacade androidModelFacade = getModelFacade(myFacet); + AndroidProject androidProject = androidModelFacade.getAndroidProject(); + if (androidProject != null) { + String compileTarget = androidProject.getCompileTarget(); AndroidVersion version = AndroidTargetHash.getPlatformVersion(compileTarget); if (version != null) { return version.getApiLevel(); @@ -848,24 +848,14 @@ class IntellijLintProject extends Project { @Nullable @Override public AndroidProject getGradleProjectModel() { - IdeaAndroidProject project = myFacet.getIdeaAndroidProject(); - if (project != null) { - project.getSelectedVariant(); - return project.getDelegate(); - } - - return null; + AndroidModelFacade androidModelFacade = getModelFacade(myFacet); + return androidModelFacade.getAndroidProject(); } @Nullable @Override public Variant getCurrentVariant() { - IdeaAndroidProject project = myFacet.getIdeaAndroidProject(); - if (project != null) { - return project.getSelectedVariant(); - } - - return null; + return getModelFacade(myFacet).getSelectedVariant(); } @Nullable @@ -879,8 +869,9 @@ class IntellijLintProject extends Project { public Boolean dependsOn(@NonNull String artifact) { if (SUPPORT_LIB_ARTIFACT.equals(artifact)) { if (mSupportLib == null) { - if (myFacet.isGradleProject() && myFacet.getIdeaAndroidProject() != null) { - mSupportLib = GradleUtil.dependsOn(myFacet.getIdeaAndroidProject(), artifact); + AndroidModelFacade facade = getModelFacade(myFacet); + if (isGradleModule(myFacet) && facade.isModelReady()) { + mSupportLib = facade.getDependsOn(artifact); } else { mSupportLib = depsDependsOn(this, artifact); } @@ -888,8 +879,9 @@ class IntellijLintProject extends Project { return mSupportLib; } else if (APPCOMPAT_LIB_ARTIFACT.equals(artifact)) { if (mAppCompat == null) { - if (myFacet.isGradleProject() && myFacet.getIdeaAndroidProject() != null) { - mAppCompat = GradleUtil.dependsOn(myFacet.getIdeaAndroidProject(), artifact); + AndroidModelFacade facade = getModelFacade(myFacet); + if (isGradleModule(myFacet) && facade.isModelReady()) { + mAppCompat = facade.getDependsOn(artifact); } else { mAppCompat = depsDependsOn(this, artifact); } @@ -897,8 +889,9 @@ class IntellijLintProject extends Project { return mAppCompat; } else { // Some other (not yet directly cached result) - if (myFacet.isGradleProject() && myFacet.getIdeaAndroidProject() != null - && GradleUtil.dependsOn(myFacet.getIdeaAndroidProject(), artifact)) { + AndroidModelFacade facade = getModelFacade(myFacet); + if (isGradleModule(myFacet) && facade.isModelReady() + && facade.getDependsOn(artifact)) { return true; } diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java index 9eca40210f7..896764b2a31 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java @@ -22,7 +22,11 @@ import com.android.builder.model.SourceProvider; import com.android.tools.klint.client.api.LintRequest; import com.google.common.base.Splitter; import com.intellij.debugger.engine.JVMNameUtil; +import com.intellij.facet.Facet; import com.intellij.ide.util.JavaAnonymousClassesHelper; +import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; +import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.io.FileUtil; @@ -61,6 +65,8 @@ public class IntellijLintUtils { @NonNls public static final String SUPPRESS_WARNINGS_FQCN = "java.lang.SuppressWarnings"; + private static final ProjectSystemId GRADLE_ID = new ProjectSystemId("GRADLE"); + /** * Gets the location of the given element * @@ -362,13 +368,33 @@ public class IntellijLintUtils { return true; } + public static boolean isProjectReady(AndroidFacet facet) { + try { + return AndroidFacet.class.getMethod("getIdeaAndroidProject", AndroidFacet.class).invoke(facet) != null; + } catch (Exception e) { + try { + return AndroidFacet.class.getMethod("getAndroidModel", AndroidFacet.class).invoke(facet) != null; + } catch (Exception ignored) {} + } + return false; + } + + public static AndroidModelFacade getModelFacade(AndroidFacet facet) { + return new AndroidModelFacade(facet); + } + + public static boolean isGradleModule(Facet facet) { + Module module = facet.getModule(); + return ExternalSystemApiUtil.isExternalSystemAwareModule(GRADLE_ID, module); + } + /** Returns the resource directories to use for the given module */ @NotNull public static List getResourceDirectories(@NotNull AndroidFacet facet) { - if (facet.isGradleProject()) { + if (isGradleModule(facet)) { List resDirectories = new ArrayList(); resDirectories.addAll(facet.getMainSourceProvider().getResDirectories()); - List flavorSourceProviders = facet.getFlavorSourceProviders(); + List flavorSourceProviders = getModelFacade(facet).getFlavorSourceProviders(); if (flavorSourceProviders != null) { for (SourceProvider provider : flavorSourceProviders) { for (File file : provider.getResDirectories()) { @@ -379,7 +405,7 @@ public class IntellijLintUtils { } } - SourceProvider buildTypeSourceProvider = facet.getBuildTypeSourceProvider(); + SourceProvider buildTypeSourceProvider = getModelFacade(facet).getBuildTypeSourceProvider(); if (buildTypeSourceProvider != null) { for (File file : buildTypeSourceProvider.getResDirectories()) { if (file.isDirectory()) {