package de.plushnikov.intellij.plugin; import com.intellij.compiler.CompilerConfiguration; import com.intellij.compiler.options.AnnotationProcessorsConfigurable; import com.intellij.notification.Notification; import com.intellij.notification.NotificationDisplayType; import com.intellij.notification.NotificationGroup; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderEntry; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiPackage; import de.plushnikov.intellij.plugin.settings.ProjectSettings; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.event.HyperlinkEvent; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Shows notifications about project setup issues, that make the plugin not working. * * @author Alexej Kubarev */ public class LombokPluginProjectValidatorComponent extends AbstractProjectComponent { private Project project; public LombokPluginProjectValidatorComponent(Project project) { super(project); this.project = project; } @NotNull @Override public String getComponentName() { return "lombok.ProjectValidatorComponent"; } @Override public void projectOpened() { // If plugin is not enabled - no point to continue if (!ProjectSettings.isLombokEnabledInProject(project)) { return; } NotificationGroup group = new NotificationGroup(Version.PLUGIN_NAME, NotificationDisplayType.BALLOON, true); // Lombok dependency check boolean hasLombokLibrary = hasLombokLibrary(project); // If dependency is missing and missing dependency notification setting is enabled (defaults to disabled) if (!hasLombokLibrary && ProjectSettings.isEnabled(project, ProjectSettings.IS_MISSING_LOMBOK_CHECK_ENABLED, false)) { Notification notification = group.createNotification(LombokBundle.message("config.warn.dependency.missing.title"), LombokBundle.message("config.warn.dependency.missing.message", project.getName()), NotificationType.ERROR, NotificationListener.URL_OPENING_LISTENER); Notifications.Bus.notify(notification, project); } // If dependency is present and out of date notification setting is enabled (defaults to disabled) if (hasLombokLibrary && ProjectSettings.isEnabled(project, ProjectSettings.IS_LOMBOK_VERSION_CHECK_ENABLED, false)) { final ModuleManager moduleManager = ModuleManager.getInstance(project); for (Module module : moduleManager.getModules()) { String lombokVersion = parseLombokVersion(findLombokEntry(ModuleRootManager.getInstance(module))); if (null != lombokVersion && compareVersionString(lombokVersion, Version.LAST_LOMBOK_VERSION) < 0) { Notification notification = group.createNotification(LombokBundle.message("config.warn.dependency.outdated.title"), LombokBundle.message("config.warn.dependency.outdated.message", project.getName(), module.getName(), lombokVersion, Version.LAST_LOMBOK_VERSION), NotificationType.WARNING, NotificationListener.URL_OPENING_LISTENER); Notifications.Bus.notify(notification, project); } } } // Annotation Processing check boolean annotationProcessorsEnabled = hasAnnotationProcessorsEnabled(project); if (!annotationProcessorsEnabled) { String annotationProcessorsConfigName = new AnnotationProcessorsConfigurable(project).getDisplayName(); Notification notification = group.createNotification(LombokBundle.message("config.warn.annotation-processing.disabled.title"), LombokBundle.message("config.warn.annotation-processing.disabled.message", project.getName()), NotificationType.ERROR, new SettingsOpeningListener(project, annotationProcessorsConfigName)); Notifications.Bus.notify(notification, project); } } private boolean hasAnnotationProcessorsEnabled(Project project) { final CompilerConfiguration config = CompilerConfiguration.getInstance(project); boolean enabled = true; final ModuleManager moduleManager = ModuleManager.getInstance(project); for (Module module : moduleManager.getModules()) { if (ModuleRootManager.getInstance(module).getSourceRoots().length > 0) { enabled &= config.getAnnotationProcessingConfiguration(module).isEnabled(); } } return enabled; } private boolean hasLombokLibrary(Project project) { PsiPackage lombokPackage = JavaPsiFacade.getInstance(project).findPackage("lombok"); return lombokPackage != null; } @Nullable private OrderEntry findLombokEntry(@NotNull ModuleRootManager moduleRootManager) { final OrderEntry[] orderEntries = moduleRootManager.getOrderEntries(); for (OrderEntry orderEntry : orderEntries) { if (orderEntry.getPresentableName().contains("lombok")) { return orderEntry; } } return null; } @Nullable protected String parseLombokVersion(@Nullable OrderEntry orderEntry) { String result = null; if (null != orderEntry) { final String presentableName = orderEntry.getPresentableName(); Pattern pattern = Pattern.compile("(.*:)([\\d\\.]+)(.*)"); final Matcher matcher = pattern.matcher(presentableName); if (matcher.find()) { result = matcher.group(2); } } return result; } protected int compareVersionString(@NotNull String firstVersionOne, @NotNull String secondVersion) { String[] firstVersionParts = firstVersionOne.split("\\."); String[] secondVersionParts = secondVersion.split("\\."); int length = Math.max(firstVersionParts.length, secondVersionParts.length); for (int i = 0; i < length; i++) { int firstPart = i < firstVersionParts.length && !firstVersionParts[i].isEmpty() ? Integer.parseInt(firstVersionParts[i]) : 0; int secondPart = i < secondVersionParts.length && !secondVersionParts[i].isEmpty() ? Integer.parseInt(secondVersionParts[i]) : 0; if (firstPart < secondPart) { return -1; } if (firstPart > secondPart) { return 1; } } return 0; } /** * Simple {@link NotificationListener.Adapter} that opens Settings Page for correct dialog. */ private static class SettingsOpeningListener extends NotificationListener.Adapter { private final Project project; private final String nameToSelect; SettingsOpeningListener(Project project, String nameToSelect) { this.project = project; this.nameToSelect = nameToSelect; } @Override protected void hyperlinkActivated(@NotNull final Notification notification, @NotNull final HyperlinkEvent e) { if (!project.isDisposed()) { ShowSettingsUtil.getInstance().showSettingsDialog(project, nameToSelect); } } } }