blob: 4b43599efe9fb563330d24dd716b084569f43b35 [file] [log] [blame]
/*
* Copyright 2000-2014 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 com.intellij.openapi.projectRoots.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.projectRoots.ProjectJdkTable;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkAdditionalData;
import com.intellij.openapi.projectRoots.SdkType;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import com.intellij.util.NullableConsumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author yole
*/
public class SdkConfigurationUtil {
private SdkConfigurationUtil() { }
public static void createSdk(@Nullable final Project project,
final Sdk[] existingSdks,
final NullableConsumer<Sdk> onSdkCreatedCallBack,
final boolean createIfExists,
final SdkType... sdkTypes) {
if (sdkTypes.length == 0) {
onSdkCreatedCallBack.consume(null);
return;
}
FileChooserDescriptor descriptor = createCompositeDescriptor(sdkTypes);
String suggestedPath = sdkTypes[0].suggestHomePath();
VirtualFile suggestedDir = suggestedPath == null ? null : LocalFileSystem.getInstance().findFileByPath(suggestedPath);
FileChooser.chooseFiles(descriptor, project, suggestedDir, new FileChooser.FileChooserConsumer() {
@Override
public void consume(List<VirtualFile> selectedFiles) {
for (SdkType sdkType : sdkTypes) {
final String path = selectedFiles.get(0).getPath();
if (sdkType.isValidSdkHome(path)) {
Sdk newSdk = null;
if (!createIfExists) {
for (Sdk sdk : existingSdks) {
if (path.equals(sdk.getHomePath())) {
newSdk = sdk;
break;
}
}
}
if (newSdk == null) {
newSdk = setupSdk(existingSdks, selectedFiles.get(0), sdkType, false, null, null);
}
onSdkCreatedCallBack.consume(newSdk);
return;
}
}
onSdkCreatedCallBack.consume(null);
}
@Override
public void cancelled() {
onSdkCreatedCallBack.consume(null);
}
});
}
public static void createSdk(@Nullable final Project project,
final Sdk[] existingSdks,
final NullableConsumer<Sdk> onSdkCreatedCallBack,
final SdkType... sdkTypes) {
createSdk(project, existingSdks, onSdkCreatedCallBack, true, sdkTypes);
}
private static FileChooserDescriptor createCompositeDescriptor(final SdkType... sdkTypes) {
return new FileChooserDescriptor(sdkTypes[0].getHomeChooserDescriptor()) {
@Override
public void validateSelectedFiles(final VirtualFile[] files) throws Exception {
if (files.length > 0) {
for (SdkType type : sdkTypes) {
if (type.isValidSdkHome(files[0].getPath())) {
return;
}
}
}
String key = files.length > 0 && files[0].isDirectory() ? "sdk.configure.home.invalid.error" : "sdk.configure.home.file.invalid.error";
throw new Exception(ProjectBundle.message(key, sdkTypes[0].getPresentableName()));
}
};
}
public static void addSdk(@NotNull final Sdk sdk) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
ProjectJdkTable.getInstance().addJdk(sdk);
}
});
}
public static void removeSdk(final Sdk sdk) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
ProjectJdkTable.getInstance().removeJdk(sdk);
}
});
}
@Nullable
public static Sdk setupSdk(final Sdk[] allSdks,
final VirtualFile homeDir, final SdkType sdkType, final boolean silent,
@Nullable final SdkAdditionalData additionalData,
@Nullable final String customSdkSuggestedName) {
final List<Sdk> sdksList = Arrays.asList(allSdks);
final ProjectJdkImpl sdk;
try {
String sdkPath = sdkType.sdkPath(homeDir);
final String sdkName = customSdkSuggestedName == null
? createUniqueSdkName(sdkType, sdkPath, sdksList)
: createUniqueSdkName(customSdkSuggestedName, sdksList);
sdk = new ProjectJdkImpl(sdkName, sdkType);
if (additionalData != null) {
// additional initialization.
// E.g. some ruby sdks must be initialized before
// setupSdkPaths() method invocation
sdk.setSdkAdditionalData(additionalData);
}
sdk.setHomePath(sdkPath);
sdkType.setupSdkPaths(sdk);
}
catch (Exception e) {
if (!silent) {
Messages.showErrorDialog("Error configuring SDK: " +
e.getMessage() +
".\nPlease make sure that " +
FileUtil.toSystemDependentName(homeDir.getPath()) +
" is a valid home path for this SDK type.", "Error Configuring SDK");
}
return null;
}
return sdk;
}
public static void setDirectoryProjectSdk(@NotNull final Project project, @Nullable final Sdk sdk) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
ProjectRootManager.getInstance(project).setProjectSdk(sdk);
final Module[] modules = ModuleManager.getInstance(project).getModules();
if (modules.length > 0) {
ModuleRootModificationUtil.setSdkInherited(modules[0]);
}
}
});
}
public static void configureDirectoryProjectSdk(final Project project,
@Nullable Comparator<Sdk> preferredSdkComparator,
final SdkType... sdkTypes) {
Sdk existingSdk = ProjectRootManager.getInstance(project).getProjectSdk();
if (existingSdk != null && ArrayUtil.contains(existingSdk.getSdkType(), sdkTypes)) {
return;
}
Sdk sdk = findOrCreateSdk(preferredSdkComparator, sdkTypes);
if (sdk != null) {
setDirectoryProjectSdk(project, sdk);
}
}
@Nullable
public static Sdk findOrCreateSdk(@Nullable Comparator<Sdk> comparator, final SdkType... sdkTypes) {
final Project defaultProject = ProjectManager.getInstance().getDefaultProject();
final Sdk sdk = ProjectRootManager.getInstance(defaultProject).getProjectSdk();
if (sdk != null) {
for (SdkType type : sdkTypes) {
if (sdk.getSdkType() == type) {
return sdk;
}
}
}
for (SdkType type : sdkTypes) {
List<Sdk> sdks = ProjectJdkTable.getInstance().getSdksOfType(type);
if (!sdks.isEmpty()) {
if (comparator != null) {
Collections.sort(sdks, comparator);
}
return sdks.get(0);
}
}
for (SdkType sdkType : sdkTypes) {
final String suggestedHomePath = sdkType.suggestHomePath();
if (suggestedHomePath != null && sdkType.isValidSdkHome(suggestedHomePath)) {
Sdk an_sdk = createAndAddSDK(suggestedHomePath, sdkType);
if (an_sdk != null) return an_sdk;
}
}
return null;
}
/**
* Tries to create an SDK identified by path; if successful, add the SDK to the global SDK table.
*
* @param path identifies the SDK
* @param sdkType
* @return newly created SDK, or null.
*/
@Nullable
public static Sdk createAndAddSDK(final String path, SdkType sdkType) {
VirtualFile sdkHome = ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
@Override
public VirtualFile compute() {
return LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
}
});
if (sdkHome != null) {
final Sdk newSdk = setupSdk(ProjectJdkTable.getInstance().getAllJdks(), sdkHome, sdkType, true, null, null);
if (newSdk != null) {
addSdk(newSdk);
}
return newSdk;
}
return null;
}
public static String createUniqueSdkName(SdkType type, String home, final Collection<Sdk> sdks) {
return createUniqueSdkName(type.suggestSdkName(null, home), sdks);
}
public static String createUniqueSdkName(final String suggestedName, final Collection<Sdk> sdks) {
final Set<String> names = new HashSet<String>();
for (Sdk jdk : sdks) {
names.add(jdk.getName());
}
String newSdkName = suggestedName;
int i = 0;
while (names.contains(newSdkName)) {
newSdkName = suggestedName + " (" + (++i) + ")";
}
return newSdkName;
}
public static void selectSdkHome(final SdkType sdkType, @NotNull final Consumer<String> consumer) {
final FileChooserDescriptor descriptor = sdkType.getHomeChooserDescriptor();
if (ApplicationManager.getApplication().isUnitTestMode()) {
Sdk sdk = ProjectJdkTable.getInstance().findMostRecentSdkOfType(sdkType);
if (sdk == null) throw new RuntimeException("No SDK of type " + sdkType + " found");
consumer.consume(sdk.getHomePath());
return;
}
FileChooser.chooseFiles(descriptor, null, getSuggestedSdkRoot(sdkType), new Consumer<List<VirtualFile>>() {
@Override
public void consume(final List<VirtualFile> chosen) {
final String path = chosen.get(0).getPath();
if (sdkType.isValidSdkHome(path)) {
consumer.consume(path);
return;
}
final String adjustedPath = sdkType.adjustSelectedSdkHome(path);
if (sdkType.isValidSdkHome(adjustedPath)) {
consumer.consume(adjustedPath);
}
}
});
}
@Nullable
public static VirtualFile getSuggestedSdkRoot(SdkType sdkType) {
final String homePath = sdkType.suggestHomePath();
return homePath == null ? null : LocalFileSystem.getInstance().findFileByPath(homePath);
}
public static List<String> filterExistingPaths(SdkType sdkType, Collection<String> sdkHomes, final Sdk[] sdks) {
List<String> result = new ArrayList<String>();
for (String sdkHome : sdkHomes) {
if (findByPath(sdkType, sdks, sdkHome) == null) {
result.add(sdkHome);
}
}
return result;
}
@Nullable
private static Sdk findByPath(SdkType sdkType, Sdk[] sdks, String sdkHome) {
for (Sdk sdk : sdks) {
final String path = sdk.getHomePath();
if (sdk.getSdkType() == sdkType && path != null &&
FileUtil.pathsEqual(FileUtil.toSystemIndependentName(path), FileUtil.toSystemIndependentName(sdkHome))) {
return sdk;
}
}
return null;
}
}