summaryrefslogtreecommitdiff
path: root/android
diff options
context:
space:
mode:
authorFuyao Zhao <fuyaoz@google.com>2015-08-04 01:27:51 +0000
committerandroid-build-merger <android-build-merger@google.com>2015-08-04 01:27:51 +0000
commit57f67ee0ca9ba217690441df6ed18b43978489b3 (patch)
tree582dc83fd3a215867cde0669f6cb80a76cf9a3bf /android
parentbc5daa9ecc873eefbda1f9bcacddd9cbc750b76c (diff)
parent42dc035d82d3d4fc7173995ee95617dddd02ea83 (diff)
downloadidea-57f67ee0ca9ba217690441df6ed18b43978489b3.tar.gz
Merge "Implement gradle aware \'Add library depedency\'." into studio-1.4-dev automerge: bf7d61f
automerge: 42dc035 * commit '42dc035d82d3d4fc7173995ee95617dddd02ea83': Implement gradle aware 'Add library depedency'.
Diffstat (limited to 'android')
-rw-r--r--android/guiTestSrc/com/android/tools/idea/tests/gui/gradle/AddGradleDependencyTest.java36
-rw-r--r--android/src/com/android/tools/idea/quickfix/AddGradleLibraryDependencyFix.java192
-rw-r--r--android/src/com/android/tools/idea/quickfix/AndroidUnresolvedReferenceQuickFixProvider.java71
3 files changed, 290 insertions, 9 deletions
diff --git a/android/guiTestSrc/com/android/tools/idea/tests/gui/gradle/AddGradleDependencyTest.java b/android/guiTestSrc/com/android/tools/idea/tests/gui/gradle/AddGradleDependencyTest.java
index 964c264d3d2..ff70110236c 100644
--- a/android/guiTestSrc/com/android/tools/idea/tests/gui/gradle/AddGradleDependencyTest.java
+++ b/android/guiTestSrc/com/android/tools/idea/tests/gui/gradle/AddGradleDependencyTest.java
@@ -27,8 +27,12 @@ import org.junit.Test;
import java.io.File;
import java.io.IOException;
+import static com.android.SdkConstants.FN_BUILD_GRADLE;
import static com.android.tools.idea.tests.gui.framework.TestGroup.PROJECT_SUPPORT;
+import static com.intellij.openapi.util.io.FileUtil.appendToFile;
+import static com.intellij.openapi.util.io.FileUtil.join;
import static com.intellij.vcsUtil.VcsUtil.getFileContent;
+import static org.fest.assertions.Assertions.assertThat;
import static org.junit.Assert.assertTrue;
@BelongsToTestGroups({PROJECT_SUPPORT})
@@ -57,6 +61,38 @@ public class AddGradleDependencyTest extends GuiTestCase {
assertBuildFileContains(projectFrame, "app/build.gradle", "androidTestCompile project(':library3')");
}
+ @Test @IdeGuiTest
+ public void testAddLibDependencyDeclaredInJavaProject() throws IOException {
+ IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("MultiModule");
+ File buildFile = new File(projectFrame.getProjectPath(), join("library3", FN_BUILD_GRADLE));
+ assertThat(buildFile).isFile();
+ appendToFile(buildFile, "dependencies { compile 'com.google.guava:guava:18.0' }");
+ projectFrame.requestProjectSync();
+
+ EditorFixture editor = projectFrame.getEditor();
+ editor.open("app/src/androidTest/java/com/android/multimodule/ApplicationTest.java");
+ typeImportAndInvokeAction(projectFrame, "com.android.multimodule;\n^","import com.google.common.base.Obje^cts;",
+ "Add library 'com.google.guava:guava:18.0' to classpath");
+
+ assertBuildFileContains(projectFrame, "app/build.gradle", "compile 'com.google.guava:guava:18.0'");
+ }
+
+ @Test @IdeGuiTest
+ public void testAddLibDependencyDeclaredInAndroidProject() throws IOException {
+ IdeFrameFixture projectFrame = importProjectAndWaitForProjectSyncToFinish("MultiModule");
+ File buildFile = new File(projectFrame.getProjectPath(), join("app", FN_BUILD_GRADLE));
+ assertThat(buildFile).isFile();
+ appendToFile(buildFile, "dependencies { compile 'com.google.guava:guava:18.0' }");
+ projectFrame.requestProjectSync();
+
+ EditorFixture editor = projectFrame.getEditor();
+ editor.open("library3/src/main/java/com/example/MyLibrary.java");
+ typeImportAndInvokeAction(projectFrame, "package com.example;\n^", "import com.google.common.base.Obje^cts;",
+ "Add library 'com.google.guava:guava:18.0' to classpath");
+
+ assertBuildFileContains(projectFrame, "app/build.gradle", "compile 'com.google.guava:guava:18.0'");
+ }
+
private static void typeImportAndInvokeAction(@NotNull IdeFrameFixture projectFrame, @NotNull String lineToType,
@NotNull String testImportStatement, @NotNull String intention) {
EditorFixture editor = projectFrame.getEditor();
diff --git a/android/src/com/android/tools/idea/quickfix/AddGradleLibraryDependencyFix.java b/android/src/com/android/tools/idea/quickfix/AddGradleLibraryDependencyFix.java
new file mode 100644
index 00000000000..f4fc7d7aa49
--- /dev/null
+++ b/android/src/com/android/tools/idea/quickfix/AddGradleLibraryDependencyFix.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 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.idea.quickfix;
+
+import com.android.builder.model.*;
+import com.android.tools.idea.gradle.IdeaAndroidProject;
+import com.android.tools.idea.gradle.parser.Dependency;
+import com.android.tools.idea.gradle.project.GradleProjectImporter;
+import com.android.tools.idea.gradle.project.GradleSyncListener;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.intellij.codeInsight.daemon.QuickFixBundle;
+import com.intellij.codeInsight.daemon.impl.actions.AddImportAction;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.LibraryOrderEntry;
+import com.intellij.openapi.roots.OrderRootType;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import org.jetbrains.android.facet.AndroidFacet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * Quickfix to add dependency to another library in gradle.build file and sync the project.
+ */
+public class AddGradleLibraryDependencyFix extends GradleDependencyFix {
+ @NotNull private final LibraryOrderEntry myLibraryEntry;
+ @NotNull private final Module myCurrentModule;
+ @NotNull private final PsiClass myClass;
+ @NotNull private final PsiReference myReference;
+ @Nullable private final String myLibraryGradleEntry;
+
+ public AddGradleLibraryDependencyFix(@NotNull LibraryOrderEntry libraryEntry, @NotNull Module currentModule, @NotNull PsiClass aCLass,
+ @NotNull PsiReference reference) {
+ myLibraryEntry = libraryEntry;
+ myCurrentModule = currentModule;
+ myClass = aCLass;
+ myReference = reference;
+ myLibraryGradleEntry = getLibraryGradleEntry();
+ }
+
+ @Override
+ @NotNull
+ public String getText() {
+ return QuickFixBundle.message("orderEntry.fix.add.library.to.classpath", myLibraryGradleEntry);
+ }
+
+ @Override
+ @NotNull
+ public String getFamilyName() {
+ return QuickFixBundle.message("orderEntry.fix.family.add.library.to.classpath");
+ }
+
+ @Override
+ public boolean isAvailable(@NotNull Project project, @Nullable Editor editor, @Nullable PsiFile file) {
+ return !project.isDisposed() && !myCurrentModule.isDisposed() && myLibraryEntry.isValid() && myLibraryGradleEntry != null;
+ }
+
+ @Override
+ public void invoke(@NotNull final Project project, @Nullable final Editor editor, @Nullable PsiFile file) {
+ if (myLibraryGradleEntry == null) {
+ return;
+ }
+ final Dependency dependency = new Dependency(getDependencyScope(myCurrentModule, false), Dependency.Type.EXTERNAL,
+ myLibraryGradleEntry);
+
+ final Application application = ApplicationManager.getApplication();
+ application.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ application.runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ addDependency(myCurrentModule, dependency);
+ gradleSyncAndImportClass(myCurrentModule, editor, myReference, new Function<Void, List<PsiClass>>() {
+ @Override
+ public List<PsiClass> apply(@Nullable Void input) {
+ return ImmutableList.of(myClass);
+ }
+ });
+ }
+ });
+ }
+ }, application.getDefaultModalityState());
+ }
+
+ /**
+ * Given a library entry, find out its corresponded gradle dependency entry like 'group:name:version".
+ */
+ @Nullable
+ private String getLibraryGradleEntry() {
+ AndroidFacet androidFacet = AndroidFacet.getInstance(myLibraryEntry.getOwnerModule());
+
+ String result = null;
+ if (androidFacet != null) {
+ result = getLibraryGradleEntryByAndroidFacet(androidFacet);
+ }
+ if (result == null) {
+ result = getLibraryGradleEntryByExaminingPath();
+ }
+ return result;
+ }
+
+ @Nullable
+ private String getLibraryGradleEntryByAndroidFacet(@NotNull AndroidFacet androidFacet) {
+ IdeaAndroidProject androidProject = androidFacet.getAndroidModel();
+ if (androidProject == null) {
+ return null;
+ }
+
+ Variant selectedVariant = androidProject.getSelectedVariant();
+ BaseArtifact testArtifact = androidProject.findSelectedTestArtifactInSelectedVariant();
+
+ Library matchedLibrary = null;
+ if (testArtifact != null) {
+ matchedLibrary = findMatchedibrary(testArtifact);
+ }
+ if (matchedLibrary == null) {
+ matchedLibrary = findMatchedibrary(selectedVariant.getMainArtifact());
+ }
+ if (matchedLibrary == null) {
+ return null;
+ }
+
+ // TODO use getRequestedCoordinates once the interface is fixed.
+ MavenCoordinates mavenCoordinates = matchedLibrary.getResolvedCoordinates();
+ if (mavenCoordinates == null) {
+ return null;
+ }
+ return mavenCoordinates.getGroupId() + ":" + mavenCoordinates.getArtifactId() + ":" + mavenCoordinates.getVersion();
+ }
+
+ @Nullable
+ Library findMatchedibrary(@NotNull BaseArtifact artifact) {
+ for (JavaLibrary library : artifact.getDependencies().getJavaLibraries()) {
+ String libraryName = FileUtil.getNameWithoutExtension(library.getJarFile());
+ if (libraryName.equals(myLibraryEntry.getLibraryName())) {
+ return library;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gradle dependencies are stored in following path: xxx/:groupId/:artifactId/:version/xxx/:artifactId-:version.jar
+ * therefor, if we can't get the artifact information from model, then try to extract from path.
+ */
+ @Nullable
+ private String getLibraryGradleEntryByExaminingPath() {
+ VirtualFile file = myLibraryEntry.getFiles(OrderRootType.CLASSES)[0];
+ String libraryName = myLibraryEntry.getLibraryName();
+ if (libraryName == null) {
+ return null;
+ }
+ String[] splittedPath = file.getPath().split(System.getProperty("file.separator"));
+
+ for (int i = 1; i < splittedPath.length - 2; i++) {
+ if (libraryName.startsWith(splittedPath[i])) {
+ String groupId = splittedPath[i - 1];
+ String artifactId = splittedPath[i];
+ String version = splittedPath[i + 1];
+ if (libraryName.endsWith(version)) {
+ return groupId + ":" + artifactId + ":" + version;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/android/src/com/android/tools/idea/quickfix/AndroidUnresolvedReferenceQuickFixProvider.java b/android/src/com/android/tools/idea/quickfix/AndroidUnresolvedReferenceQuickFixProvider.java
index d5e301109a9..00dd73d9643 100644
--- a/android/src/com/android/tools/idea/quickfix/AndroidUnresolvedReferenceQuickFixProvider.java
+++ b/android/src/com/android/tools/idea/quickfix/AndroidUnresolvedReferenceQuickFixProvider.java
@@ -17,6 +17,7 @@ package com.android.tools.idea.quickfix;
import com.android.tools.idea.gradle.facet.AndroidGradleFacet;
import com.android.tools.idea.gradle.parser.GradleBuildFile;
+import com.google.common.collect.Sets;
import com.intellij.codeInsight.daemon.QuickFixActionRegistrar;
import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix;
import com.intellij.codeInsight.intention.IntentionAction;
@@ -24,18 +25,19 @@ import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
import com.intellij.jarFinder.FindJarFix;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.*;
+import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.packageDependencies.DependencyValidationManager;
-import com.intellij.psi.PsiClass;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiFile;
-import com.intellij.psi.PsiJavaCodeReferenceElement;
+import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import static com.android.tools.idea.gradle.util.GradleUtil.getGradleBuildFile;
import static com.intellij.openapi.module.ModuleUtilCore.findModuleForPsiElement;
@@ -43,12 +45,11 @@ import static com.intellij.openapi.module.ModuleUtilCore.findModuleForPsiElement
public class AndroidUnresolvedReferenceQuickFixProvider extends UnresolvedReferenceQuickFixProvider<PsiJavaCodeReferenceElement> {
@Override
- public void registerFixes(@NotNull PsiJavaCodeReferenceElement reference, @NotNull QuickFixActionRegistrar registrar) {
+ public void registerFixes(final @NotNull PsiJavaCodeReferenceElement reference, @NotNull QuickFixActionRegistrar registrar) {
Module contextModule = findModuleForPsiElement(reference);
if (contextModule == null) {
return;
}
-
AndroidGradleFacet gradleFacet = AndroidGradleFacet.getInstance(contextModule);
if (gradleFacet == null) {
return;
@@ -63,6 +64,10 @@ public class AndroidUnresolvedReferenceQuickFixProvider extends UnresolvedRefere
if (contextFile == null) {
return;
}
+ final VirtualFile classVFile = contextFile.getVirtualFile();
+ if (classVFile == null) {
+ return;
+ }
// Since this is a gradle android project, we need to unregister:
// "add module dependency fix",
@@ -82,8 +87,7 @@ public class AndroidUnresolvedReferenceQuickFixProvider extends UnresolvedRefere
return;
}
- // TODO implement a quickfix that could properly "add junit dependency", "add library dependency" to the gradle file.
-
+ // TODO implement a quickfix that could properly "add junit dependency" to the gradle file.
PsiElement psiElement = reference.getElement();
String referenceName = reference.getRangeInElement().substring(psiElement.getText());
Project project = psiElement.getProject();
@@ -94,8 +98,57 @@ public class AndroidUnresolvedReferenceQuickFixProvider extends UnresolvedRefere
if (!allowedDependencies.isEmpty()) {
classes = allowedDependencies.toArray(new PsiClass[allowedDependencies.size()]);
- registrar.register(new AddGradleProjectDependencyFix(contextModule, contextFile.getVirtualFile(), classes, reference));
+ registrar.register(new AddGradleProjectDependencyFix(contextModule, classVFile, classes, reference));
+ }
+
+ JavaPsiFacade facade = JavaPsiFacade.getInstance(psiElement.getProject());
+ ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
+
+ Set<Object> librariesToAdd = Sets.newHashSet();
+ for (PsiClass aClass : classes) {
+ if (!facade.getResolveHelper().isAccessible(aClass, psiElement, aClass)) {
+ continue;
+ }
+ PsiFile psiFile = aClass.getContainingFile();
+ if (psiFile == null) {
+ continue;
+ }
+ VirtualFile virtualFile = psiFile.getVirtualFile();
+ if (virtualFile == null) {
+ continue;
+ }
+ ModuleFileIndex moduleFileIndex = ModuleRootManager.getInstance(contextModule).getFileIndex();
+ for (OrderEntry orderEntry : fileIndex.getOrderEntriesForFile(virtualFile)) {
+ if (orderEntry instanceof LibraryOrderEntry) {
+ LibraryOrderEntry libraryEntry = (LibraryOrderEntry)orderEntry;
+ Library library = libraryEntry.getLibrary();
+ if (library == null) {
+ continue;
+ }
+ VirtualFile[] files = library.getFiles(OrderRootType.CLASSES);
+ if (files.length == 0) {
+ continue;
+ }
+ final VirtualFile jar = files[0];
+
+ if (jar == null || libraryEntry.isModuleLevel() && !librariesToAdd.add(jar) || !librariesToAdd.add(library)) {
+ continue;
+ }
+ OrderEntry entryForFile = moduleFileIndex.getOrderEntryForFile(virtualFile);
+ if (entryForFile != null) {
+ if (entryForFile instanceof ExportableOrderEntry &&
+ ((ExportableOrderEntry)entryForFile).getScope() == DependencyScope.TEST &&
+ !ModuleRootManager.getInstance(contextModule).getFileIndex().isInTestSourceContent(classVFile)) {
+ }
+ else {
+ continue;
+ }
+ }
+ registrar.register(new AddGradleLibraryDependencyFix(libraryEntry, contextModule, aClass, reference));
+ }
+ }
}
+
}
// Duplicated from com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix.filterAllowedDependencies