| /* |
| * 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.util.io.FileUtil.notNullize; |
| 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.openapi.projectRoots.impl.SdkConfigurationUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.pom.java.LanguageLevel; |
| import com.intellij.util.SystemProperties; |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Comparator; |
| import java.util.Iterator; |
| import java.util.List; |
| import javax.annotation.Nullable; |
| |
| /** Utility methods related to IDEA JDKs. */ |
| public class Jdks { |
| |
| private static final Logger logger = Logger.getInstance(Jdks.class); |
| |
| @Nullable |
| public static Sdk chooseOrCreateJavaSdk(LanguageLevel langLevel) { |
| Sdk existing = findClosestMatch(langLevel); |
| if (existing != null) { |
| return existing; |
| } |
| String jdkHomePath = null; |
| for (DefaultSdkProvider defaultSdkProvider : DefaultSdkProvider.EP_NAME.getExtensions()) { |
| File sdkRoot = defaultSdkProvider.provideSdkForLanguage(LanguageClass.JAVA); |
| if (sdkRoot != null) { |
| jdkHomePath = sdkRoot.getPath(); |
| break; |
| } |
| } |
| |
| if (jdkHomePath == null) { |
| jdkHomePath = getJdkHomePath(langLevel); |
| } |
| return jdkHomePath != null ? createJdk(jdkHomePath) : null; |
| } |
| |
| @Nullable |
| @VisibleForTesting |
| static Sdk findClosestMatch(LanguageLevel langLevel) { |
| return Arrays.stream(ProjectJdkTable.getInstance().getAllJdks()) |
| .filter( |
| sdk -> { |
| LanguageLevel level = getJavaLanguageLevel(sdk); |
| return level != null && level.isAtLeast(langLevel); |
| }) |
| .min(Comparator.comparing(Jdks::getJavaLanguageLevel)) |
| .orElse(null); |
| } |
| |
| /** |
| * Returns null if the SDK is not a java JDK, or doesn't have a recognized java langauge level. |
| */ |
| @Nullable |
| private static LanguageLevel getJavaLanguageLevel(Sdk sdk) { |
| if (!(sdk.getSdkType() instanceof JavaSdk)) { |
| return null; |
| } |
| JavaSdkVersion version = JavaSdk.getInstance().getVersion(sdk); |
| return version != null ? version.getMaxLanguageLevel() : null; |
| } |
| |
| @Nullable |
| private static String getJdkHomePath(LanguageLevel langLevel) { |
| Collection<String> jdkHomePaths = new ArrayList<>(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 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); |
| } |
| |
| @Nullable |
| private static String getBestJdkHomePath(List<String> jdkHomePaths, 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 (StringUtil.isNotEmpty(jdkHomePath)) { |
| roots.add(jdkHomePath); |
| roots.addAll(getChildrenPaths(jdkHomePath)); |
| } |
| } |
| return getBestJdk(roots, langLevel); |
| } |
| |
| private static List<String> getChildrenPaths(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(List<String> jdkRoots, LanguageLevel langLevel) { |
| return jdkRoots |
| .stream() |
| .filter(root -> JavaSdk.getInstance().isValidSdkHome(root)) |
| .filter(root -> getVersion(root).getMaxLanguageLevel().isAtLeast(langLevel)) |
| .min(Comparator.comparing(o -> getVersion(o).getMaxLanguageLevel())) |
| .orElse(null); |
| } |
| |
| private static JavaSdkVersion getVersion(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 |
| private static Sdk createJdk(String jdkHomePath) { |
| Sdk jdk = SdkConfigurationUtil.createAndAddSDK(jdkHomePath, JavaSdk.getInstance()); |
| if (jdk == null) { |
| logger.error(String.format("Unable to create JDK from path '%1$s'", jdkHomePath)); |
| } |
| return jdk; |
| } |
| } |