1818
1919import com .github .benmanes .caffeine .cache .Caffeine ;
2020import com .github .benmanes .caffeine .cache .LoadingCache ;
21- import com .google .common .annotations .VisibleForTesting ;
2221import com .google .common .base .Preconditions ;
2322import com .intellij .ide .plugins .IdeaPluginDescriptor ;
2423import com .intellij .ide .plugins .PluginManager ;
25- import com .intellij .openapi .application .ApplicationInfo ;
2624import com .intellij .openapi .extensions .PluginId ;
2725import com .intellij .openapi .project .Project ;
28- import com .intellij .openapi .projectRoots .JdkUtil ;
29- import com .intellij .openapi .projectRoots .Sdk ;
30- import com .intellij .openapi .roots .ProjectRootManager ;
31- import com .intellij .openapi .util .BuildNumber ;
3226import com .intellij .openapi .util .SystemInfo ;
3327import com .palantir .javaformat .bootstrap .BootstrappingFormatterService ;
3428import com .palantir .javaformat .bootstrap .NativeImageFormatterService ;
3529import com .palantir .javaformat .java .FormatterService ;
3630import java .io .IOException ;
3731import java .io .UncheckedIOException ;
38- import java .net .MalformedURLException ;
3932import java .net .URI ;
40- import java .net .URL ;
41- import java .net .URLClassLoader ;
4233import java .nio .file .Files ;
4334import java .nio .file .Path ;
4435import java .util .List ;
4536import java .util .Objects ;
4637import java .util .Optional ;
47- import java .util .OptionalInt ;
48- import java .util .ServiceLoader ;
49- import java .util .jar .Attributes .Name ;
5038import java .util .stream .Collectors ;
5139import java .util .stream .Stream ;
5240import org .slf4j .Logger ;
@@ -57,7 +45,7 @@ final class FormatterProvider {
5745
5846 static final String PLUGIN_ID = "open-java-format" ;
5947
60- // Cache to avoid creating a URLClassloader every time we want to format from IntelliJ
48+ // Cache to avoid resolving the formatter every time we want to format from IntelliJ
6149 private final LoadingCache <FormatterCacheKey , Optional <FormatterService >> implementationCache =
6250 Caffeine .newBuilder ().maximumSize (1 ).build (FormatterProvider ::createFormatter );
6351
@@ -70,7 +58,6 @@ static IdeaPluginDescriptor getPluginDescriptor() {
7058 Optional <FormatterService > get (Project project , PalantirJavaFormatSettings settings ) {
7159 return implementationCache .get (new FormatterCacheKey (
7260 project ,
73- getSdkVersion (project ),
7461 settings .getImplementationClassPath (),
7562 settings .getNativeImageClassPath (),
7663 settings .injectedVersionIsOutdated ()));
@@ -82,39 +69,16 @@ private static Optional<FormatterService> createFormatter(FormatterCacheKey cach
8269 log .info ("Using the native formatter with classpath: {}" , cacheKey .nativeImageClassPath .get ());
8370 return Optional .of (new NativeImageFormatterService (Path .of (cacheKey .nativeImageClassPath .get ())));
8471 }
85- if (cacheKey .jdkMajorVersion .isEmpty ()) {
86- return Optional .empty ();
87- }
8872
89- int jdkMajorVersion = cacheKey .jdkMajorVersion .getAsInt ();
73+ // The formatter runs in a JVM of its own, started with the "--add-exports" it needs to reach javac.
74+ // That JVM is the IDE's own runtime, never the project SDK: every IDE this plugin supports runs on Java 21
75+ // or later, which the formatter needs, while a project SDK can be any version.
76+ int jdkMajorVersion = Runtime .version ().feature ();
77+ Path jdkPath = Path .of (System .getProperty ("java.home" ), "bin" , SystemInfo .isWindows ? "java.exe" : "java" );
9078 List <Path > implementationClasspath =
9179 getImplementationUrls (cacheKey .implementationClassPath , cacheKey .useBundledImplementation );
92-
93- // When running with JDK 15+ or using newer language features, we use the bootstrapping formatter which injects
94- // required "--add-exports" args.
95- if (useBootstrappingFormatter (
96- jdkMajorVersion , ApplicationInfo .getInstance ().getBuild ())) {
97- Path jdkPath = getJdkPath (cacheKey .project );
98- log .info ("Using bootstrapping formatter with jdk version {} and path: {}" , jdkMajorVersion , jdkPath );
99- return Optional .of (new BootstrappingFormatterService (jdkPath , jdkMajorVersion , implementationClasspath ));
100- }
101-
102- // Use "in-process" formatter service
103- log .info ("Using in-process formatter for jdk version {}" , jdkMajorVersion );
104- URL [] implementationUrls = toUrlsUnchecked (implementationClasspath );
105- ClassLoader classLoader = new URLClassLoader (implementationUrls , FormatterService .class .getClassLoader ());
106- return ServiceLoader .load (FormatterService .class , classLoader ).findFirst ();
107- }
108-
109- /**
110- * When projects use JDK 15+ as their SDK, they might use newer language features which are only supported by the
111- * bootstrapping formatter.
112- * Separately, starting from 2022.2 (branch number '222'), Intellij now runs with JDK 17 which also requires the
113- * bootstrapping formatter. See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html for
114- * how the build number is formatted.
115- */
116- private static boolean useBootstrappingFormatter (int jdkMajorVersion , BuildNumber buildNumber ) {
117- return jdkMajorVersion >= 15 || buildNumber .getBaselineVersion () >= 222 ;
80+ log .info ("Using bootstrapping formatter with jdk version {} and path: {}" , jdkMajorVersion , jdkPath );
81+ return Optional .of (new BootstrappingFormatterService (jdkPath , jdkMajorVersion , implementationClasspath ));
11882 }
11983
12084 private static List <Path > getProvidedImplementationUrls (List <URI > implementationClasspath ) {
@@ -149,68 +113,6 @@ private static List<Path> getImplementationUrls(
149113 });
150114 }
151115
152- private static Path getJdkPath (Project project ) {
153- return getProjectJdk (project )
154- .map (Sdk ::getHomePath )
155- .map (Path ::of )
156- .map (sdkHome -> sdkHome .resolve ("bin" ).resolve ("java" + (SystemInfo .isWindows ? ".exe" : "" )))
157- .filter (Files ::exists )
158- .orElseThrow (() ->
159- new IllegalStateException ("Could not determine JDK path for project: " + project .getName ()));
160- }
161-
162- private static OptionalInt getSdkVersion (Project project ) {
163- return getProjectJdk (project )
164- .map (FormatterProvider ::parseSdkJavaVersion )
165- .orElseThrow (() ->
166- // This is not that common as our Gradle infrastructure should setup an SDK, but it does
167- // happen on occassion and it manifests as the plugin ceasing to format. Give a slight
168- // nudge for the manual remediation.
169- new IllegalStateException ("Could not determine SDK version for project: " + project .getName ()
170- + ". Ensure you have an SDK set in "
171- + "'Project Structure' -> 'Project Settings' -> 'Project' -> 'SDK'" ));
172- }
173-
174- private static OptionalInt parseSdkJavaVersion (Sdk sdk ) {
175- // Parses the actual version out of "SDK#getVersionString" which returns 'java version "15"'
176- // or 'openjdk version "15.0.2"'.
177- @ SuppressWarnings ("for-rollout:deprecation" )
178- String version = Preconditions .checkNotNull (
179- JdkUtil .getJdkMainAttribute (sdk , Name .IMPLEMENTATION_VERSION ), "JDK version is null" );
180- return parseSdkJavaVersion (version );
181- }
182-
183- @ SuppressWarnings ("for-rollout:Slf4jLogsafeArgs" )
184- @ VisibleForTesting
185- static OptionalInt parseSdkJavaVersion (String version ) {
186- int indexOfVersionDelimiter = version .indexOf ('.' );
187- String normalizedVersion =
188- indexOfVersionDelimiter >= 0 ? version .substring (0 , indexOfVersionDelimiter ) : version ;
189- normalizedVersion = normalizedVersion .replaceAll ("-ea" , "" );
190- try {
191- return OptionalInt .of (Integer .parseInt (normalizedVersion ));
192- } catch (NumberFormatException e ) {
193- log .error ("Could not parse sdk version: {}" , version , e );
194- return OptionalInt .empty ();
195- }
196- }
197-
198- private static Optional <Sdk > getProjectJdk (Project project ) {
199- return Optional .ofNullable (ProjectRootManager .getInstance (project ).getProjectSdk ());
200- }
201-
202- private static URL [] toUrlsUnchecked (List <Path > paths ) {
203- return paths .stream ()
204- .map (path -> {
205- try {
206- return path .toUri ().toURL ();
207- } catch (IllegalArgumentException | MalformedURLException e ) {
208- throw new RuntimeException ("Couldn't convert Path to URL: " + path , e );
209- }
210- })
211- .toArray (URL []::new );
212- }
213-
214116 private static List <Path > listDirAsUrlsUnchecked (Path dir ) {
215117 try (Stream <Path > list = Files .list (dir )) {
216118 return list .collect (Collectors .toList ());
@@ -221,19 +123,16 @@ private static List<Path> listDirAsUrlsUnchecked(Path dir) {
221123
222124 private static final class FormatterCacheKey {
223125 private final Project project ;
224- private final OptionalInt jdkMajorVersion ;
225126 private final Optional <List <URI >> implementationClassPath ;
226127 private final Optional <URI > nativeImageClassPath ;
227128 private final boolean useBundledImplementation ;
228129
229130 FormatterCacheKey (
230131 Project project ,
231- OptionalInt jdkMajorVersion ,
232132 Optional <List <URI >> implementationClassPath ,
233133 Optional <URI > nativeImageClassPath ,
234134 boolean useBundledImplementation ) {
235135 this .project = project ;
236- this .jdkMajorVersion = jdkMajorVersion ;
237136 this .implementationClassPath = implementationClassPath ;
238137 this .nativeImageClassPath = nativeImageClassPath ;
239138 this .useBundledImplementation = useBundledImplementation ;
@@ -248,17 +147,15 @@ public boolean equals(Object o) {
248147 return false ;
249148 }
250149 FormatterCacheKey that = (FormatterCacheKey ) o ;
251- return Objects .equals (jdkMajorVersion , that .jdkMajorVersion )
252- && useBundledImplementation == that .useBundledImplementation
150+ return useBundledImplementation == that .useBundledImplementation
253151 && Objects .equals (project , that .project )
254152 && Objects .equals (implementationClassPath , that .implementationClassPath )
255153 && Objects .equals (nativeImageClassPath , that .nativeImageClassPath );
256154 }
257155
258156 @ Override
259157 public int hashCode () {
260- return Objects .hash (
261- project , jdkMajorVersion , implementationClassPath , nativeImageClassPath , useBundledImplementation );
158+ return Objects .hash (project , implementationClassPath , nativeImageClassPath , useBundledImplementation );
262159 }
263160 }
264161}
0 commit comments