1 /* 2 * Copyright 2023 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.googlejavaformat.intellij; 18 19 import com.google.common.base.Suppliers; 20 import com.intellij.ide.ui.IdeUiService; 21 import com.intellij.notification.Notification; 22 import com.intellij.notification.NotificationType; 23 import com.intellij.openapi.diagnostic.Logger; 24 import com.intellij.openapi.project.Project; 25 import java.util.function.Supplier; 26 27 class JreConfigurationChecker { 28 29 private static final Supplier<Boolean> hasAccess = 30 Suppliers.memoize(JreConfigurationChecker::checkJreConfiguration); 31 private static final Logger logger = Logger.getInstance(JreConfigurationChecker.class); 32 33 private final Project project; 34 JreConfigurationChecker(Project project)35 public JreConfigurationChecker(Project project) { 36 this.project = project; 37 } 38 checkJreConfiguration(Project project)39 static boolean checkJreConfiguration(Project project) { 40 var success = hasAccess.get(); 41 if (!success) { 42 project.getService(JreConfigurationChecker.class).displayConfigurationErrorNotification(); 43 } 44 return success; 45 } 46 47 /** 48 * Determine whether the JRE is configured to work with the google-java-format plugin. If not, 49 * display a notification with instructions and return false. 50 */ checkJreConfiguration()51 private static boolean checkJreConfiguration() { 52 try { 53 return testClassAccess( 54 "com.sun.tools.javac.api.JavacTrees", 55 "com.sun.tools.javac.code.Flags", 56 "com.sun.tools.javac.file.JavacFileManager", 57 "com.sun.tools.javac.parser.JavacParser", 58 "com.sun.tools.javac.tree.JCTree", 59 "com.sun.tools.javac.util.Log"); 60 } catch (ClassNotFoundException e) { 61 logger.error("Error checking jre configuration for google-java-format", e); 62 return false; 63 } 64 } 65 testClassAccess(String... classNames)66 private static boolean testClassAccess(String... classNames) throws ClassNotFoundException { 67 for (String className : classNames) { 68 if (!testClassAccess(className)) { 69 return false; 70 } 71 } 72 return true; 73 } 74 testClassAccess(String className)75 private static boolean testClassAccess(String className) throws ClassNotFoundException { 76 Class<?> klass = Class.forName(className); 77 return klass 78 .getModule() 79 // isExported returns true if the package is either open or exported. Either one is 80 // sufficient 81 // to run the google-java-format code (even though the documentation specifies --add-opens). 82 .isExported( 83 klass.getPackageName(), 84 JreConfigurationChecker.class.getClassLoader().getUnnamedModule()); 85 } 86 displayConfigurationErrorNotification()87 private void displayConfigurationErrorNotification() { 88 Notification notification = 89 new Notification( 90 "Configure JRE for google-java-format", 91 "Configure the JRE for google-java-format", 92 "The google-java-format plugin needs additional configuration before it can be used. " 93 + "<a href=\"instructions\">Follow the instructions here</a>.", 94 NotificationType.INFORMATION); 95 notification.setListener( 96 (n, e) -> { 97 IdeUiService.getInstance() 98 .browse( 99 "https://github.com/google/google-java-format/blob/master/README.md#intellij-jre-config"); 100 n.expire(); 101 }); 102 notification.notify(project); 103 } 104 } 105