blob: e80c98bac6a0cbd5f2646b8c4b18932077640b70 [file] [log] [blame]
/*
* Copyright 2016 The Bazel Authors. All rights reserved.
*
* 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.google.idea.blaze.java.sync.projectstructure;
import static com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil.createAndAddSDK;
import static com.intellij.openapi.util.io.FileUtil.notNullize;
import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
import static java.util.Collections.emptyList;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.model.primitives.LanguageClass;
import com.google.idea.blaze.base.sync.sdk.DefaultSdkProvider;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.ProjectJdkTable;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.util.SystemProperties;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/** Utility methods related to IDEA JDKs. */
public class Jdks {
@NonNls private static final LanguageLevel DEFAULT_LANG_LEVEL = LanguageLevel.JDK_1_7;
@Nullable
public static Sdk chooseOrCreateJavaSdk(LanguageLevel langLevel) {
for (Sdk sdk : ProjectJdkTable.getInstance().getAllJdks()) {
if (isApplicableJdk(sdk, langLevel)) {
return sdk;
}
}
String jdkHomePath = null;
for (DefaultSdkProvider defaultSdkProvider : DefaultSdkProvider.EP_NAME.getExtensions()) {
File sdk = defaultSdkProvider.provideSdkForLanguage(LanguageClass.JAVA);
if (sdk != null) {
jdkHomePath = sdk.getPath();
break;
}
}
if (jdkHomePath == null) {
jdkHomePath = getJdkHomePath(langLevel);
}
if (jdkHomePath == null) {
return null;
}
return createJdk(jdkHomePath);
}
public static boolean isApplicableJdk(@NotNull Sdk jdk, @Nullable LanguageLevel langLevel) {
if (!(jdk.getSdkType() instanceof JavaSdk)) {
return false;
}
if (langLevel == null) {
langLevel = DEFAULT_LANG_LEVEL;
}
JavaSdkVersion version = JavaSdk.getInstance().getVersion(jdk);
if (version != null) {
//noinspection TestOnlyProblems
return hasMatchingLangLevel(version, langLevel);
}
return false;
}
@Nullable
public static String getJdkHomePath(@NotNull LanguageLevel langLevel) {
Collection<String> jdkHomePaths =
new ArrayList<String>(JavaSdk.getInstance().suggestHomePaths());
if (jdkHomePaths.isEmpty()) {
return null;
}
// prefer jdk path of getJavaHome(), since we have to allow access to it in tests
// see AndroidProjectDataServiceTest#testImportData()
final List<String> list = new ArrayList<String>();
String javaHome = SystemProperties.getJavaHome();
if (javaHome != null && !javaHome.isEmpty()) {
for (Iterator<String> it = jdkHomePaths.iterator(); it.hasNext(); ) {
final String path = it.next();
if (path != null && javaHome.startsWith(path)) {
it.remove();
list.add(path);
}
}
}
list.addAll(jdkHomePaths);
return getBestJdkHomePath(list, langLevel);
}
@VisibleForTesting
@Nullable
static String getBestJdkHomePath(
@NotNull Collection<String> jdkHomePaths, @NotNull LanguageLevel langLevel) {
// Search for JDKs in both the suggest folder and all its sub folders.
List<String> roots = Lists.newArrayList();
for (String jdkHomePath : jdkHomePaths) {
if (isNotEmpty(jdkHomePath)) {
roots.add(jdkHomePath);
roots.addAll(getChildrenPaths(jdkHomePath));
}
}
return getBestJdk(roots, langLevel);
}
@NotNull
private static List<String> getChildrenPaths(@NotNull String dirPath) {
File dir = new File(dirPath);
if (!dir.isDirectory()) {
return emptyList();
}
List<String> childrenPaths = Lists.newArrayList();
for (File child : notNullize(dir.listFiles())) {
boolean directory = child.isDirectory();
if (directory) {
childrenPaths.add(child.getAbsolutePath());
}
}
return childrenPaths;
}
@Nullable
private static String getBestJdk(
@NotNull List<String> jdkRoots, @NotNull LanguageLevel langLevel) {
String bestJdk = null;
for (String jdkRoot : jdkRoots) {
if (JavaSdk.getInstance().isValidSdkHome(jdkRoot)) {
if (bestJdk == null && hasMatchingLangLevel(jdkRoot, langLevel)) {
bestJdk = jdkRoot;
} else if (bestJdk != null) {
bestJdk = selectJdk(bestJdk, jdkRoot, langLevel);
}
}
}
return bestJdk;
}
@Nullable
private static String selectJdk(
@NotNull String jdk1, @NotNull String jdk2, @NotNull LanguageLevel langLevel) {
if (hasMatchingLangLevel(jdk1, langLevel)) {
return jdk1;
}
if (hasMatchingLangLevel(jdk2, langLevel)) {
return jdk2;
}
return null;
}
private static boolean hasMatchingLangLevel(
@NotNull String jdkRoot, @NotNull LanguageLevel langLevel) {
JavaSdkVersion version = getVersion(jdkRoot);
return hasMatchingLangLevel(version, langLevel);
}
@VisibleForTesting
static boolean hasMatchingLangLevel(
@NotNull JavaSdkVersion jdkVersion, @NotNull LanguageLevel langLevel) {
LanguageLevel max = jdkVersion.getMaxLanguageLevel();
return max.isAtLeast(langLevel);
}
@NotNull
private static JavaSdkVersion getVersion(@NotNull String jdkRoot) {
String version = JavaSdk.getInstance().getVersionString(jdkRoot);
if (version == null) {
return JavaSdkVersion.JDK_1_0;
}
JavaSdkVersion sdkVersion = JavaSdk.getInstance().getVersion(version);
return sdkVersion == null ? JavaSdkVersion.JDK_1_0 : sdkVersion;
}
@Nullable
public static Sdk createJdk(@NotNull String jdkHomePath) {
Sdk jdk = createAndAddSDK(jdkHomePath, JavaSdk.getInstance());
if (jdk == null) {
String msg = String.format("Unable to create JDK from path '%1$s'", jdkHomePath);
Logger.getInstance(Jdks.class).error(msg);
}
return jdk;
}
}