blob: 900b7c283d2a6f76c74d8887254b55549eb2f1d3 [file] [log] [blame]
/*
* 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.dependencies.impl;
import com.android.tools.idea.dependencies.DependencyManager;
import com.android.tools.idea.gradle.parser.BuildFileKey;
import com.android.tools.idea.gradle.parser.BuildFileStatement;
import com.android.tools.idea.gradle.parser.Dependency;
import com.android.tools.idea.gradle.parser.GradleBuildFile;
import com.android.tools.idea.gradle.project.GradleProjectImporter;
import com.android.tools.idea.gradle.project.GradleSyncListener;
import com.android.tools.idea.templates.RepositoryUrlManager;
import com.google.common.collect.Lists;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import static com.intellij.openapi.util.text.StringUtil.pluralize;
public class GradleDependencyManager extends DependencyManager {
private static final String ADD_DEPENDENCY = "Add Dependency";
@NotNull
public static GradleDependencyManager getInstance(@NotNull Project project) {
return ServiceManager.getService(project, GradleDependencyManager.class);
}
@NotNull
@Override
public List<String> findMissingDependencies(@NotNull Module module, @NotNull Iterable<String> androidDependencies) {
GradleBuildFile gradleBuildFile = GradleBuildFile.get(module);
assert gradleBuildFile != null;
return findMissingLibrariesFromGradleBuildFile(gradleBuildFile, androidDependencies);
}
@Override
public boolean ensureLibraryIsIncluded(@NotNull Module module,
@NotNull Iterable<String> androidDependencies,
@Nullable Runnable callback) {
GradleBuildFile gradleBuildFile = GradleBuildFile.get(module);
assert gradleBuildFile != null;
List<String> missing = findMissingLibrariesFromGradleBuildFile(gradleBuildFile, androidDependencies);
if (missing.isEmpty()) {
return true;
}
if (userWantToAddDependencies(module, missing)) {
addDependenciesInTransaction(gradleBuildFile, module, missing, callback);
}
return false;
}
private static List<String> findMissingLibrariesFromGradleBuildFile(@NotNull GradleBuildFile gradleBuildFile,
@NotNull Iterable<String> pathIds) {
List<String> missingLibraries = Lists.newArrayList();
RepositoryUrlManager manager = RepositoryUrlManager.get();
for (String pathId : pathIds) {
String libraryCoordinate = manager.getLibraryCoordinate(pathId);
boolean dependencyFound = false;
for (BuildFileStatement entry : gradleBuildFile.getDependencies()) {
if (entry instanceof Dependency) {
Dependency dependency = (Dependency)entry;
if (dependency.scope == Dependency.Scope.COMPILE &&
dependency.type == Dependency.Type.EXTERNAL &&
dependency.getValueAsString().equals(libraryCoordinate)) {
dependencyFound = true;
break;
}
}
}
if (!dependencyFound) {
missingLibraries.add(pathId);
}
}
return missingLibraries;
}
private static boolean userWantToAddDependencies(@NotNull Module module, @NotNull List<String> missing) {
String libraryNames = StringUtil.join(missing, ", ");
String message = String.format("This operation requires the %1$s: %2$s. \n\nWould you like to add %3$s %1$s now?",
pluralize("library", missing.size()), libraryNames, pluralize("this", missing.size()));
Project project = module.getProject();
return Messages.showOkCancelDialog(project, message, "Add Project Dependency", Messages.getErrorIcon()) == Messages.OK;
}
private static void addDependenciesInTransaction(@NotNull final GradleBuildFile gradleBuildFile,
@NotNull final Module module,
@NotNull List<String> missing,
@Nullable final Runnable callback) {
assert !missing.isEmpty();
final List<String> missingLibraryCoordinates = Lists.newArrayList();
RepositoryUrlManager manager = RepositoryUrlManager.get();
for (String pathId : missing) {
missingLibraryCoordinates.add(manager.getLibraryCoordinate(pathId));
}
final Project project = module.getProject();
new WriteCommandAction(project, ADD_DEPENDENCY) {
@Override
protected void run(@NotNull final Result result) throws Throwable {
addDependencies(gradleBuildFile, module, missingLibraryCoordinates);
GradleProjectImporter.getInstance().requestProjectSync(project, false /* do not generate sources */, createSyncListener(callback));
}
}.execute();
}
private static void addDependencies(@NotNull final GradleBuildFile gradleBuildFile,
@NotNull Module module,
@NotNull final List<String> libraryCoordinates) {
ModuleRootModificationUtil.updateModel(module, new Consumer<ModifiableRootModel>() {
@Override
public void consume(ModifiableRootModel model) {
List<BuildFileStatement> dependencies = gradleBuildFile.getDependencies();
for (String libraryCoordinate : libraryCoordinates) {
dependencies.add(new Dependency(Dependency.Scope.COMPILE, Dependency.Type.EXTERNAL, libraryCoordinate));
}
gradleBuildFile.setValue(BuildFileKey.DEPENDENCIES, dependencies);
}
});
}
@Nullable
private static GradleSyncListener createSyncListener(@Nullable final Runnable callback) {
if (callback == null) {
return null;
}
return new GradleSyncListener() {
@Override
public void syncStarted(@NotNull Project project) {
}
@Override
public void syncSucceeded(@NotNull Project project) {
callback.run();
}
@Override
public void syncFailed(@NotNull Project project, @NotNull String errorMessage) {
}
@Override
public void syncSkipped(@NotNull Project project) {
callback.run();
}
};
}
}