blob: 36e936a331d84a49a22a6e87b95282a23597784b [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 com.android.tools.idea.gradle.project;
import com.android.tools.idea.gradle.GradleSyncState;
import com.android.tools.idea.gradle.IdeaAndroidProject;
import com.android.tools.idea.gradle.IdeaGradleProject;
import com.android.tools.idea.gradle.IdeaJavaProject;
import com.android.tools.idea.gradle.facet.AndroidGradleFacet;
import com.android.tools.idea.gradle.facet.JavaGradleFacet;
import com.android.tools.idea.gradle.invoker.GradleInvoker;
import com.android.tools.idea.gradle.util.LocalProperties;
import com.android.tools.idea.sdk.IdeSdks;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode;
import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback;
import com.intellij.openapi.externalSystem.util.ExternalSystemBundle;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.CompilerProjectExtension;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.util.SystemProperties;
import org.jetbrains.android.AndroidPlugin.GuiTestSuiteState;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.newProject.AndroidModuleBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings;
import org.jetbrains.plugins.gradle.settings.GradleSettings;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import static com.android.SdkConstants.FN_BUILD_GRADLE;
import static com.android.tools.idea.gradle.AndroidProjectKeys.*;
import static com.android.tools.idea.gradle.project.LibraryAttachments.removeLibrariesAndStoreAttachments;
import static com.android.tools.idea.gradle.project.NewProjectImportGradleSyncListener.createTopLevelProjectAndOpen;
import static com.android.tools.idea.gradle.project.SdkSync.syncIdeAndProjectAndroidSdks;
import static com.android.tools.idea.gradle.util.FilePaths.pathToIdeaUrl;
import static com.android.tools.idea.gradle.util.GradleUtil.*;
import static com.android.tools.idea.gradle.util.Projects.*;
import static com.android.tools.idea.startup.AndroidStudioSpecificInitializer.isAndroidStudio;
import static com.google.common.base.Strings.nullToEmpty;
import static com.intellij.notification.NotificationType.ERROR;
import static com.intellij.openapi.externalSystem.model.ProjectKeys.MODULE;
import static com.intellij.openapi.externalSystem.service.execution.ExternalSystemJdkUtil.checkForJdk;
import static com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode.IN_BACKGROUND_ASYNC;
import static com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode.MODAL_SYNC;
import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.find;
import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.findAll;
import static com.intellij.openapi.externalSystem.util.ExternalSystemUtil.refreshProject;
import static com.intellij.openapi.project.ProjectTypeService.setProjectType;
import static com.intellij.openapi.ui.Messages.showErrorDialog;
import static com.intellij.openapi.util.io.FileUtil.*;
import static com.intellij.openapi.util.io.FileUtilRt.createIfNotExists;
import static com.intellij.openapi.vfs.VfsUtilCore.virtualToIoFile;
import static com.intellij.ui.AppUIUtil.invokeLaterIfProjectAlive;
import static com.intellij.util.ui.UIUtil.invokeAndWaitIfNeeded;
import static org.jetbrains.android.AndroidPlugin.getGuiTestSuiteState;
import static org.jetbrains.android.AndroidPlugin.isGuiTestingMode;
/**
* Imports an Android-Gradle project without showing the "Import Project" Wizard UI.
*/
public class GradleProjectImporter {
private static final Logger LOG = Logger.getInstance(GradleProjectImporter.class);
// When this system property is set, the sync operation always tries to use the cached project data unless any gradle files are modified.
private static final boolean SYNC_WITH_CACHED_MODEL_ONLY =
SystemProperties.getBooleanProperty("studio.sync.with.cached.model.only", false);
private final ImporterDelegate myDelegate;
/**
* Flag used by unit tests to selectively disable code which requires an open project or UI updates; this is used
* by unit tests that do not run all of IntelliJ (e.g. do not extend the IdeaTestCase base)
*/
public static boolean ourSkipSetupFromTest;
@NotNull
public static GradleProjectImporter getInstance() {
return ServiceManager.getService(GradleProjectImporter.class);
}
public GradleProjectImporter() {
myDelegate = new ImporterDelegate();
}
@VisibleForTesting
GradleProjectImporter(ImporterDelegate delegate) {
myDelegate = delegate;
}
/**
* Imports the given Gradle project.
*
* @param selectedFile the selected build.gradle or the project's root directory.
*/
public void importProject(@NotNull VirtualFile selectedFile) {
VirtualFile projectDir = selectedFile.isDirectory() ? selectedFile : selectedFile.getParent();
File projectDirPath = virtualToIoFile(projectDir);
// Sync Android SDKs paths *before* importing project. Studio will freeze if the project has a local.properties file pointing to a SDK
// path that does not exist. The cause is that having 2 dialogs: one modal (the "Project Import" one) and another from
// Messages.showErrorDialog (indicating the Android SDK path does not exist) produce a deadlock.
try {
LocalProperties localProperties = new LocalProperties(projectDirPath);
if (isAndroidStudio()) {
syncIdeAndProjectAndroidSdks(localProperties);
}
}
catch (IOException e) {
LOG.info("Failed to sync SDKs", e);
showErrorDialog(e.getMessage(), "Project Import");
return;
}
// Set up Gradle settings. Otherwise we get an "already disposed project" error.
new GradleSettings(ProjectManager.getInstance().getDefaultProject());
createProjectFileForGradleProject(selectedFile, null);
}
/**
* Creates IntelliJ project file in the root of the project directory.
*
* @param selectedFile build.gradle in the module folder.
* @param project existing parent project or {@code null} if a new one should be created.
*/
private void createProjectFileForGradleProject(@NotNull VirtualFile selectedFile, @Nullable Project project) {
VirtualFile projectDir = selectedFile.isDirectory() ? selectedFile : selectedFile.getParent();
File projectDirPath = virtualToIoFile(projectDir);
try {
importProject(projectDir.getName(), projectDirPath, true, new NewProjectImportGradleSyncListener() {
@Override
public void syncSucceeded(@NotNull Project project) {
activateProjectView(project);
}
}, project, null);
}
catch (Exception e) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
throw new RuntimeException(e);
}
showErrorDialog(e.getMessage(), "Project Import");
LOG.error(e);
}
}
/**
* Requests a project sync with Gradle. If the project import is successful,
* {@link com.android.tools.idea.gradle.util.ProjectBuilder#generateSourcesOnly()} will be invoked at the end.
*
* @param project the given project. This method does nothing if the project is not an Android-Gradle project.
* @param listener called after the project has been imported.
*/
public void requestProjectSync(@NotNull Project project, @Nullable GradleSyncListener listener) {
requestProjectSync(project, true, listener);
}
/**
* Requests a project sync with Gradle.
*
* @param project the given project. This method does nothing if the project is not an Android-Gradle project.
* @param generateSourcesOnSuccess indicates whether the IDE should invoke Gradle to generate Java sources after a successful project
* import.
* @param listener called after the project has been imported.
*/
public void requestProjectSync(@NotNull Project project,
boolean generateSourcesOnSuccess,
@Nullable GradleSyncListener listener) {
requestProjectSync(project, false, generateSourcesOnSuccess, listener);
}
/**
* Requests a project sync with Gradle.
*
* @param project the given project. This method does nothing if the project is not an Android-Gradle project.
* @param useCachedProjectData indicates whether the IDE should try to use the cached data or invoke Gradle to get the project data.
* This is just a suggestion and IDE can still invoke Gradle when the cached data is not available or
* no longer valid.
* @param generateSourcesOnSuccess indicates whether the IDE should invoke Gradle to generate Java sources after a successful project
* import. This applies only when the project data is obtained by Gradle invocation and sources are never
* generated when the cached project data is used.
* @param listener called after the project has been imported.
*/
public void requestProjectSync(@NotNull Project project,
boolean useCachedProjectData,
boolean generateSourcesOnSuccess,
@Nullable GradleSyncListener listener) {
Runnable syncRequest = createSyncRequest(project, IN_BACKGROUND_ASYNC, generateSourcesOnSuccess, useCachedProjectData, listener);
invokeLaterIfProjectAlive(project, syncRequest);
}
public void syncProjectSynchronously(@NotNull Project project,
boolean generateSourcesOnSuccess,
@Nullable GradleSyncListener listener) {
Runnable syncRequest = createSyncRequest(project, MODAL_SYNC, generateSourcesOnSuccess, false, listener);
invokeAndWaitIfNeeded(syncRequest);
}
@NotNull
private Runnable createSyncRequest(@NotNull final Project project,
@NotNull final ProgressExecutionMode executionMode,
final boolean generateSourcesOnSuccess,
final boolean useCachedProjectData,
@Nullable final GradleSyncListener listener) {
return new Runnable() {
@Override
public void run() {
try {
doRequestSync(project, executionMode, new ImportOptions(generateSourcesOnSuccess, false, useCachedProjectData), listener);
}
catch (ConfigurationException e) {
showErrorDialog(project, e.getMessage(), e.getTitle());
}
}
};
}
private void doRequestSync(@NotNull final Project project,
@NotNull ProgressExecutionMode progressExecutionMode,
@NotNull ImportOptions options,
@Nullable final GradleSyncListener listener) throws ConfigurationException {
if (requiresAndroidModel(project) || hasTopLevelGradleBuildFile(project)) {
FileDocumentManager.getInstance().saveAllDocuments();
setUpGradleSettings(project);
resetProject(project);
setGradleVersionUsed(project, null);
doImport(project, false /* existing project */, progressExecutionMode, options, listener);
}
else {
Runnable notificationTask = new Runnable() {
@Override
public void run() {
String msg = String.format("The project '%s' is not a Gradle-based project", project.getName());
AndroidGradleNotification.getInstance(project).showBalloon("Project Sync", msg, ERROR, new OpenMigrationToGradleUrlHyperlink());
if (listener != null) {
listener.syncFailed(project, msg);
}
}
};
Application application = ApplicationManager.getApplication();
if (application.isDispatchThread()) {
notificationTask.run();
}
else {
application.invokeLater(notificationTask);
}
}
}
private static boolean hasTopLevelGradleBuildFile(@NotNull Project project) {
VirtualFile baseDir = project.getBaseDir();
VirtualFile gradleBuildFile = baseDir.findChild(FN_BUILD_GRADLE);
return gradleBuildFile != null && gradleBuildFile.exists() && !gradleBuildFile.isDirectory();
}
// See issue: https://code.google.com/p/android/issues/detail?id=64508
private static void resetProject(@NotNull final Project project) {
executeProjectChanges(project, new Runnable() {
@Override
public void run() {
removeLibrariesAndStoreAttachments(project);
// Remove all AndroidProjects from module. Otherwise, if re-import/sync fails, editors will not show the proper notification of the
// failure.
ModuleManager moduleManager = ModuleManager.getInstance(project);
for (Module module : moduleManager.getModules()) {
AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet != null) {
facet.setAndroidModel(null);
}
}
}
});
}
/**
* Imports and opens an Android project that has been created with the "New Project" wizard. This method does not perform any project
* validation before importing the project (assuming that the wizard properly created the new project.)
*
* @param projectName name of the project.
* @param projectRootDirPath the path of the project's root directory.
* @param listener called after the project has been imported.
* @param project the given project. This method does nothing if the project is not an Android-Gradle project.
* @param initialLanguageLevel when creating a new project, sets the language level to the given version early on (this is because you
* cannot set a language level later on in the process without telling the user that the language level
* has changed and to re-open the project)
* @throws IOException if any file I/O operation fails (e.g. creating the '.idea' directory.)
* @throws ConfigurationException if any required configuration option is missing (e.g. Gradle home directory path.)
*/
public void importNewlyCreatedProject(@NotNull String projectName,
@NotNull File projectRootDirPath,
@Nullable GradleSyncListener listener,
@Nullable Project project,
@Nullable LanguageLevel initialLanguageLevel) throws IOException, ConfigurationException {
doImport(projectName, projectRootDirPath, new ImportOptions(true, false, false), listener, project, initialLanguageLevel);
}
/**
* Imports and opens an Android project.
*
* @param projectName name of the project.
* @param projectRootDirPath path of the projects' root directory.
* @param generateSourcesOnSuccess whether to generate sources after sync.
* @param listener called after the project has been imported.
* @param project the given project. This method does nothing if the project is not an Android-Gradle project.
* @param initialLanguageLevel when creating a new project, sets the language level to the given version early on (this is because you
* cannot set a language level later on in the process without telling the user that the language level
* has changed and to re-open the project)
* @throws IOException if any file I/O operation fails (e.g. creating the '.idea' directory.)
* @throws ConfigurationException if any required configuration option is missing (e.g. Gradle home directory path.)
*/
public void importProject(@NotNull String projectName,
@NotNull File projectRootDirPath,
boolean generateSourcesOnSuccess,
@Nullable GradleSyncListener listener,
@Nullable Project project,
@Nullable LanguageLevel initialLanguageLevel) throws IOException, ConfigurationException {
ImportOptions options = new ImportOptions(generateSourcesOnSuccess, true, false);
doImport(projectName, projectRootDirPath, options, listener, project, initialLanguageLevel);
}
private void doImport(@NotNull String projectName,
@NotNull File projectRootDirPath,
@NotNull ImportOptions options,
@Nullable GradleSyncListener listener,
@Nullable Project project,
@Nullable LanguageLevel initialLanguageLevel) throws IOException, ConfigurationException {
createTopLevelBuildFileIfNotExisting(projectRootDirPath);
createIdeaProjectDir(projectRootDirPath);
Project newProject = project == null ? createProject(projectName, projectRootDirPath.getPath()) : project;
if (project == null) {
GradleSettings settings = GradleSettings.getInstance(newProject);
settings.setGradleVmOptions("");
}
setUpProject(newProject, initialLanguageLevel);
if (!ApplicationManager.getApplication().isUnitTestMode()) {
newProject.save();
}
doImport(newProject, true /* new project */, MODAL_SYNC /* synchronous import */, options, listener);
}
private static void createTopLevelBuildFileIfNotExisting(@NotNull File projectRootDirPath) throws IOException {
File projectFile = getGradleBuildFilePath(projectRootDirPath);
if (projectFile.isFile()) {
return;
}
createIfNotExists(projectFile);
String contents = "// Top-level build file where you can add configuration options common to all sub-projects/modules." +
SystemProperties.getLineSeparator();
writeToFile(projectFile, contents);
}
private static void createIdeaProjectDir(@NotNull File projectRootDirPath) throws IOException {
File ideaDirPath = new File(projectRootDirPath, Project.DIRECTORY_STORE_FOLDER);
if (ideaDirPath.isDirectory()) {
// "libraries" is hard-coded in com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable
File librariesDir = new File(ideaDirPath, "libraries");
if (librariesDir.exists()) {
// remove contents of libraries. This is useful when importing existing projects that may have invalid library entries (e.g.
// created with Studio 0.4.3 or earlier.)
boolean librariesDirDeleted = delete(librariesDir);
if (!librariesDirDeleted) {
LOG.info(String.format("Failed to delete %1$s'", librariesDir.getPath()));
}
}
}
else {
ensureExists(ideaDirPath);
}
}
@NotNull
private static Project createProject(@NotNull String projectName, @NotNull String projectPath) throws ConfigurationException {
ProjectManager projectManager = ProjectManager.getInstance();
Project newProject = projectManager.createProject(projectName, projectPath);
if (newProject == null) {
throw new NullPointerException("Failed to create a new IDEA project");
}
return newProject;
}
private static void setUpProject(@NotNull final Project newProject, @Nullable final LanguageLevel initialLanguageLevel) {
CommandProcessor.getInstance().executeCommand(newProject, new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
if (initialLanguageLevel != null) {
final LanguageLevelProjectExtension extension = LanguageLevelProjectExtension.getInstance(newProject);
if (extension != null) {
extension.setLanguageLevel(initialLanguageLevel);
}
}
// In practice, it really does not matter where the compiler output folder is. Gradle handles that. This is done just to please
// IDEA.
File compilerOutputDirPath = new File(getBaseDirPath(newProject), join(BUILD_DIR_DEFAULT_NAME, "classes"));
String compilerOutputDirUrl = pathToIdeaUrl(compilerOutputDirPath);
CompilerProjectExtension compilerProjectExt = CompilerProjectExtension.getInstance(newProject);
assert compilerProjectExt != null;
compilerProjectExt.setCompilerOutputUrl(compilerOutputDirUrl);
setUpGradleSettings(newProject);
// This allows to customize UI when android project is opened inside IDEA with android plugin.
setProjectType(newProject, AndroidModuleBuilder.ANDROID_PROJECT_TYPE);
}
});
}
}, null, null);
}
private static void setUpGradleSettings(@NotNull Project project) {
GradleProjectSettings projectSettings = getGradleProjectSettings(project);
if (projectSettings == null) {
projectSettings = new GradleProjectSettings();
}
setUpGradleProjectSettings(project, projectSettings);
GradleSettings gradleSettings = GradleSettings.getInstance(project);
gradleSettings.setLinkedProjectsSettings(ImmutableList.of(projectSettings));
}
private static void setUpGradleProjectSettings(@NotNull Project project, @NotNull GradleProjectSettings settings) {
settings.setUseAutoImport(false);
// Set the JDK to use when syncing project.
if (isAndroidStudio()) {
Sdk jdk = IdeSdks.getJdk();
if (jdk != null) {
settings.setGradleJvm(jdk.getName());
}
} else {
// validate Gradle SDK
if (!checkForJdk(project, settings.getGradleJvm())) {
// Set first acceptable JDK to use when syncing project (or create one if it is not set up yet)
Sdk jdk = IdeSdks.getJdk();
if (jdk != null) {
settings.setGradleJvm(jdk.getName());
}
}
}
String basePath = project.getBasePath();
if (basePath != null) {
settings.setExternalProjectPath(toCanonicalPath(basePath));
}
}
private void doImport(@NotNull final Project project,
final boolean newProject,
@NotNull final ProgressExecutionMode progressExecutionMode,
@NotNull ImportOptions options,
@Nullable final GradleSyncListener listener) throws ConfigurationException {
if (isAndroidStudio()) {
// See https://code.google.com/p/android/issues/detail?id=169743
clearStoredGradleJvmArgs(project);
}
PreSyncChecks.PreSyncCheckResult preSyncCheckResult = PreSyncChecks.canSync(project);
if (!preSyncCheckResult.isSuccess()) {
// User should have already warned that something is not right and sync cannot continue.
GradleSyncState syncState = GradleSyncState.getInstance(project);
syncState.syncStarted(true);
createTopLevelProjectAndOpen(project);
syncState.syncFailed(nullToEmpty(preSyncCheckResult.getFailureCause()));
return;
}
if (isAndroidStudio() && isDirectGradleInvocationEnabled(project)) {
// We cannot do the same when using JPS. We don't have access to the contents of the Message view used by JPS.
// For now, we can only improve the user experience in Android Studio.
GradleInvoker.getInstance(project).clearConsoleAndBuildMessages();
}
// Prevent IDEA from syncing with Gradle. We want to have full control of syncing.
project.putUserData(ExternalSystemDataKeys.NEWLY_IMPORTED_PROJECT, true);
setHasSyncErrors(project, false);
setHasWrongJdk(project, false);
if (forceSyncWithCachedModel() || options.useCachedProjectData) {
GradleProjectSyncData syncData = GradleProjectSyncData.getInstance((project));
if (syncData != null && syncData.canUseCachedProjectData()) {
DataNode<ProjectData> cache = getCachedProjectData(project);
if (cache != null && !isCacheMissingModels(cache, project)) {
PostProjectSetupTasksExecutor executor = PostProjectSetupTasksExecutor.getInstance(project);
executor.setGenerateSourcesAfterSync(false);
executor.setUsingCachedProjectData(true);
executor.setLastSyncTimestamp(syncData.getLastGradleSyncTimestamp());
ProjectSetUpTask setUpTask = new ProjectSetUpTask(project, newProject, options.importingExistingProject, true, listener);
setUpTask.onSuccess(cache);
return;
}
}
}
// We only update UI on sync when re-importing projects. By "updating UI" we mean updating the "Build Variants" tool window and editor
// notifications. It is not safe to do this for new projects because the new project has not been opened yet.
GradleSyncState.getInstance(project).syncStarted(!newProject);
PostProjectSetupTasksExecutor.getInstance(project).setGenerateSourcesAfterSync(options.generateSourcesOnSuccess);
ProjectSetUpTask setUpTask = new ProjectSetUpTask(project, newProject, options.importingExistingProject, false, listener);
myDelegate.importProject(project, setUpTask, progressExecutionMode);
}
private static boolean forceSyncWithCachedModel() {
if (SYNC_WITH_CACHED_MODEL_ONLY) {
return true;
}
if (isGuiTestingMode()) {
GuiTestSuiteState state = getGuiTestSuiteState();
return state.syncWithCachedModelOnly();
}
return false;
}
@VisibleForTesting
static boolean isCacheMissingModels(@NotNull DataNode<ProjectData> cache, @NotNull Project project) {
Collection<DataNode<ModuleData>> moduleDataNodes = findAll(cache, MODULE);
if (!moduleDataNodes.isEmpty()) {
Map<String, DataNode<ModuleData>> moduleDataNodesByName = indexByModuleName(moduleDataNodes);
ModuleManager moduleManager = ModuleManager.getInstance(project);
for (Module module : moduleManager.getModules()) {
DataNode<ModuleData> moduleDataNode = moduleDataNodesByName.get(module.getName());
if (moduleDataNode == null) {
// When a Gradle facet is present, there should be a cache node for the module.
AndroidGradleFacet gradleFacet = AndroidGradleFacet.getInstance(module);
if (gradleFacet != null) {
return true;
}
}
else if (isCacheMissingModels(moduleDataNode, module)) {
return true;
}
}
return false;
}
return true;
}
@NotNull
private static Map<String, DataNode<ModuleData>> indexByModuleName(@NotNull Collection<DataNode<ModuleData>> moduleDataNodes) {
Map<String, DataNode<ModuleData>> mapping = Maps.newHashMap();
for (DataNode<ModuleData> moduleDataNode : moduleDataNodes) {
ModuleData data = moduleDataNode.getData();
mapping.put(data.getExternalName(), moduleDataNode);
}
return mapping;
}
private static boolean isCacheMissingModels(@NotNull DataNode<ModuleData> cache, @NotNull Module module) {
AndroidGradleFacet gradleFacet = AndroidGradleFacet.getInstance(module);
if (gradleFacet != null) {
DataNode<IdeaGradleProject> gradleDataNode = find(cache, IDE_GRADLE_PROJECT);
if (gradleDataNode == null) {
return true;
}
AndroidFacet androidFacet = AndroidFacet.getInstance(module);
if (androidFacet != null) {
DataNode<IdeaAndroidProject> androidDataNode = find(cache, IDE_ANDROID_PROJECT);
if (androidDataNode == null) {
return true;
}
}
else {
JavaGradleFacet javaFacet = JavaGradleFacet.getInstance(module);
if (javaFacet != null) {
DataNode<IdeaJavaProject> javaProjectDataNode = find(cache, IDE_JAVA_PROJECT);
if (javaProjectDataNode == null) {
return true;
}
}
}
}
return false;
}
// Makes it possible to mock invocations to the Gradle Tooling API.
static class ImporterDelegate {
void importProject(@NotNull Project project,
@NotNull ExternalProjectRefreshCallback callback,
@NotNull final ProgressExecutionMode progressExecutionMode) throws ConfigurationException {
try {
refreshProject(project, GRADLE_SYSTEM_ID, getBaseDirPath(project).getPath(), callback, false /* resolve dependencies */,
progressExecutionMode, true /* always report import errors */);
}
catch (RuntimeException e) {
String externalSystemName = GRADLE_SYSTEM_ID.getReadableName();
throw new ConfigurationException(e.getMessage(), ExternalSystemBundle.message("error.cannot.parse.project", externalSystemName));
}
}
}
private static class ImportOptions {
public final boolean generateSourcesOnSuccess;
public final boolean importingExistingProject;
public final boolean useCachedProjectData;
ImportOptions(boolean generateSourcesOnSuccess, boolean importingExistingProject, boolean useCachedProjectData) {
this.generateSourcesOnSuccess = generateSourcesOnSuccess;
this.importingExistingProject = importingExistingProject;
this.useCachedProjectData = useCachedProjectData;
}
}
}