diff --git a/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java b/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java index a8382e9df54..088ba988728 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java +++ b/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java @@ -17,6 +17,9 @@ package org.jetbrains.jet.plugin; import com.google.common.collect.Lists; +import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.ide.plugins.PluginManager; +import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; @@ -72,4 +75,11 @@ public class JetPluginUtil { } return libraryScope == ((NamespaceDescriptor) declaration).getMemberScope(); } + + @NotNull + public static String getPluginVersion() { + IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId("org.jetbrains.kotlin")); + assert plugin != null : "How can it be? Kotlin plugin is available, but its component is running. Complete nonsense."; + return plugin.getVersion(); + } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/AbsentJdkAnnotationsNotifications.java b/idea/src/org/jetbrains/jet/plugin/quickfix/AbsentJdkAnnotationsNotifications.java index 5d0dd9450d4..85dfe991c8f 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/AbsentJdkAnnotationsNotifications.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/AbsentJdkAnnotationsNotifications.java @@ -52,7 +52,7 @@ public class AbsentJdkAnnotationsNotifications extends EditorNotifications.Provi GlobalSearchScope scope = module.getModuleWithDependenciesAndLibrariesScope(false); if (JavaPsiFacade.getInstance(project).findClass("jet.JetObject", scope) == null) return null; - if (ConfigureKotlinLibraryNotificationProvider.jdkAnnotationsArePresent(module)) return null; + if (KotlinRuntimeLibraryUtil.jdkAnnotationsArePresent(module)) return null; Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); if (sdk == null) return null; @@ -62,7 +62,7 @@ public class AbsentJdkAnnotationsNotifications extends EditorNotifications.Provi panel.createActionLabel("Set up Kotlin JDK annotations", new Runnable() { @Override public void run() { - ConfigureKotlinLibraryNotificationProvider.addJdkAnnotations(module); + KotlinRuntimeLibraryUtil.addJdkAnnotations(module); } }); diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java index 7d71b1d5a38..7d658de130e 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java @@ -28,46 +28,24 @@ import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.projectRoots.SdkModificator; -import com.intellij.openapi.roots.AnnotationOrderRootType; -import com.intellij.openapi.roots.ModifiableRootModel; -import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.roots.OrderRootType; -import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; import com.intellij.openapi.roots.libraries.Library; -import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextFieldWithBrowseButton; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.JarFileSystem; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.JavaPsiFacade; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.EditorNotificationPanel; import com.intellij.ui.EditorNotifications; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; import org.jetbrains.jet.plugin.JetFileType; -import org.jetbrains.jet.utils.PathUtil; import javax.swing.*; import java.io.File; import java.io.IOException; -import static org.jetbrains.jet.plugin.project.JsModuleDetector.isJsModule; - public class ConfigureKotlinLibraryNotificationProvider extends EditorNotifications.Provider { private static final Key KEY = Key.create("configure.kotlin.library"); - public static final String LIBRARY_NAME = "KotlinRuntime"; - public static final String KOTLIN_RUNTIME_JAR = "kotlin-runtime.jar"; private final Project myProject; public ConfigureKotlinLibraryNotificationProvider(Project project) { @@ -90,7 +68,7 @@ public class ConfigureKotlinLibraryNotificationProvider extends EditorNotificati final Module module = ModuleUtilCore.findModuleForFile(file, myProject); if (module == null) return null; - if (!isModuleAlreadyConfigured(module)) { + if (!KotlinRuntimeLibraryUtil.isModuleAlreadyConfigured(module)) { return createNotificationPanel(module); } } @@ -104,57 +82,6 @@ public class ConfigureKotlinLibraryNotificationProvider extends EditorNotificati return null; } - private Library findOrCreateRuntimeLibrary() { - LibraryTable table = ProjectLibraryTable.getInstance(myProject); - Library kotlinRuntime = table.getLibraryByName(LIBRARY_NAME); - if (kotlinRuntime != null) { - for (VirtualFile root : kotlinRuntime.getFiles(OrderRootType.CLASSES)) { - if (root.getName().equals(KOTLIN_RUNTIME_JAR)) { - return kotlinRuntime; - } - } - } - - File runtimePath = PathUtil.getKotlinPathsForIdeaPlugin().getRuntimePath(); - if (!runtimePath.exists()) { - Messages.showErrorDialog(myProject, "kotlin-runtime.jar is not found. Make sure plugin is properly installed.", - "No Runtime Found"); - return null; - } - - ChoosePathDialog dlg = new ChoosePathDialog(myProject); - dlg.show(); - if (!dlg.isOK()) return null; - String path = dlg.getPath(); - final File targetJar = new File(path, "kotlin-runtime.jar"); - try { - FileUtil.copy(runtimePath, targetJar); - VirtualFile jarVfs = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(targetJar); - if (jarVfs != null) { - jarVfs.refresh(false, false); - } - } - catch (IOException e) { - Messages.showErrorDialog(myProject, "Error copying jar: " + e.getLocalizedMessage(), "Error Copying File"); - return null; - } - - if (kotlinRuntime == null) { - kotlinRuntime = table.createLibrary("KotlinRuntime"); - } - - final Library finalKotlinRuntime = kotlinRuntime; - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - Library.ModifiableModel model = finalKotlinRuntime.getModifiableModel(); - model.addRoot(VfsUtil.getUrlForLibraryRoot(targetJar), OrderRootType.CLASSES); - model.addRoot(VfsUtil.getUrlForLibraryRoot(targetJar) + "src", OrderRootType.SOURCES); - model.commit(); - } - }); - - return kotlinRuntime; - } private EditorNotificationPanel createNotificationPanel(final Module module) { final EditorNotificationPanel answer = new EditorNotificationPanel(); @@ -163,7 +90,7 @@ public class ConfigureKotlinLibraryNotificationProvider extends EditorNotificati answer.createActionLabel("Set up module '" + module.getName() + "' as JVM Kotlin module", new Runnable() { @Override public void run() { - setUpKotlinRuntime(module); + setUpJavaModule(module); } }); @@ -177,6 +104,18 @@ public class ConfigureKotlinLibraryNotificationProvider extends EditorNotificati return answer; } + private void setUpJavaModule(Module module) { + Library library = KotlinRuntimeLibraryUtil.findOrCreateRuntimeLibrary(myProject, new UiFindRuntimeLibraryHandler()); + if (library == null) return; + + KotlinRuntimeLibraryUtil.setUpKotlinRuntimeLibrary(module, library, new Runnable() { + @Override + public void run() { + updateNotifications(); + } + }); + } + private void setUpJSModule(@NotNull Module module) { JsModuleSetUp.doSetUpModule(module, new Runnable() { @Override @@ -186,56 +125,6 @@ public class ConfigureKotlinLibraryNotificationProvider extends EditorNotificati }); } - private void setUpKotlinRuntime(@NotNull final Module module) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - Library library = findOrCreateRuntimeLibrary(); - if (library != null) { - ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel(); - if (model.findLibraryOrderEntry(library) == null) { - model.addLibraryEntry(library); - model.commit(); - } - else { - model.dispose(); - } - updateNotifications(); - } - if (!jdkAnnotationsArePresent(module)) { - addJdkAnnotations(module); - } - } - }); - } - - /* package */ static void addJdkAnnotations(Module module) { - Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); - assert sdk != null; - File annotationsIoFile = PathUtil.getKotlinPathsForIdeaPlugin().getJdkAnnotationsPath(); - if (annotationsIoFile.exists()) { - VirtualFile jdkAnnotationsJar = LocalFileSystem.getInstance().findFileByIoFile(annotationsIoFile); - if (jdkAnnotationsJar != null) { - SdkModificator modificator = sdk.getSdkModificator(); - modificator.addRoot(JarFileSystem.getInstance().getJarRootForLocalFile(jdkAnnotationsJar), - AnnotationOrderRootType.getInstance()); - modificator.commitChanges(); - } - } - } - - /* package */ static boolean jdkAnnotationsArePresent(Module module) { - Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); - if (sdk == null) return false; - return ContainerUtil.exists(sdk.getRootProvider().getFiles(AnnotationOrderRootType.getInstance()), - new Condition() { - @Override - public boolean value(VirtualFile file) { - return PathUtil.JDK_ANNOTATIONS_JAR.equals(file.getName()); - } - }); - } - private void updateNotifications() { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override @@ -245,22 +134,6 @@ public class ConfigureKotlinLibraryNotificationProvider extends EditorNotificati }); } - private boolean isModuleAlreadyConfigured(Module module) { - return isMavenModule(module) || isJsModule(module) || isWithJavaModule(module); - } - - private boolean isWithJavaModule(Module module) { - // Can find a reference to kotlin class in module scope - GlobalSearchScope scope = module.getModuleWithDependenciesAndLibrariesScope(false); - return (JavaPsiFacade.getInstance(myProject).findClass(JvmStdlibNames.JET_OBJECT.getFqName().getFqName(), scope) != null); - } - - private static boolean isMavenModule(@NotNull Module module) { - // This constant could be acquired from MavenProjectsManager, but we don't want to depend on the Maven plugin... - // See MavenProjectsManager.isMavenizedModule() - return "true".equals(module.getOptionValue("org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule")); - } - private static class ChoosePathDialog extends DialogWrapper { private final Project myProject; private TextFieldWithBrowseButton myPathField; @@ -293,4 +166,27 @@ public class ConfigureKotlinLibraryNotificationProvider extends EditorNotificati return myPathField.getText(); } } + + private class UiFindRuntimeLibraryHandler extends KotlinRuntimeLibraryUtil.FindRuntimeLibraryHandler { + @Override + public void runtimePathDoesNotExist(@NotNull File path) { + Messages.showErrorDialog(myProject, + "kotlin-runtime.jar is not found at " + path + ". Make sure plugin is properly installed.", + "No Runtime Found"); + } + + @Override + public File getRuntimeJarPath() { + ChoosePathDialog dlg = new ChoosePathDialog(myProject); + dlg.show(); + if (!dlg.isOK()) return null; + String path = dlg.getPath(); + return new File(path, "kotlin-runtime.jar"); + } + + @Override + public void ioExceptionOnCopyingJar(@NotNull IOException e) { + Messages.showErrorDialog(myProject, "Error copying jar: " + e.getLocalizedMessage(), "Error Copying File"); + } + } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/KotlinRuntimeLibraryUtil.java b/idea/src/org/jetbrains/jet/plugin/quickfix/KotlinRuntimeLibraryUtil.java new file mode 100644 index 00000000000..0c3317e2d71 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/KotlinRuntimeLibraryUtil.java @@ -0,0 +1,247 @@ +/* + * Copyright 2010-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.jet.plugin.quickfix; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkModificator; +import com.intellij.openapi.roots.AnnotationOrderRootType; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.roots.libraries.LibraryTable; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.packaging.impl.elements.ManifestFileUtil; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; +import org.jetbrains.jet.utils.PathUtil; + +import java.io.File; +import java.io.IOException; +import java.util.jar.Attributes; +import java.util.jar.JarFile; + +import static org.jetbrains.jet.plugin.project.JsModuleDetector.isJsModule; + +public class KotlinRuntimeLibraryUtil { + public static final String LIBRARY_NAME = "KotlinRuntime"; + public static final String KOTLIN_RUNTIME_JAR = "kotlin-runtime.jar"; + public static final String UNKNOWN_VERSION = "UNKNOWN"; + + private KotlinRuntimeLibraryUtil() {} + + public static void addJdkAnnotations(@NotNull Module module) { + Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); + assert sdk != null; + File annotationsIoFile = PathUtil.getKotlinPathsForIdeaPlugin().getJdkAnnotationsPath(); + if (annotationsIoFile.exists()) { + VirtualFile jdkAnnotationsJar = LocalFileSystem.getInstance().findFileByIoFile(annotationsIoFile); + if (jdkAnnotationsJar != null) { + SdkModificator modificator = sdk.getSdkModificator(); + modificator.addRoot(JarFileSystem.getInstance().getJarRootForLocalFile(jdkAnnotationsJar), + AnnotationOrderRootType.getInstance()); + modificator.commitChanges(); + } + } + } + + public static boolean jdkAnnotationsArePresent(@NotNull Module module) { + Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); + if (sdk == null) return false; + return ContainerUtil.exists(sdk.getRootProvider().getFiles(AnnotationOrderRootType.getInstance()), + new Condition() { + @Override + public boolean value(VirtualFile file) { + return PathUtil.JDK_ANNOTATIONS_JAR.equals(file.getName()); + } + }); + } + + public static boolean isModuleAlreadyConfigured(Module module) { + return isMavenModule(module) || isJsModule(module) || isWithJavaModule(module); + } + + private static boolean isMavenModule(@NotNull Module module) { + // This constant could be acquired from MavenProjectsManager, but we don't want to depend on the Maven plugin... + // See MavenProjectsManager.isMavenizedModule() + return "true".equals(module.getOptionValue("org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule")); + } + + private static boolean isWithJavaModule(Module module) { + // Can find a reference to kotlin class in module scope + GlobalSearchScope scope = module.getModuleWithDependenciesAndLibrariesScope(false); + return (JavaPsiFacade.getInstance(module.getProject()).findClass(JvmStdlibNames.JET_OBJECT.getFqName().getFqName(), scope) != null); + } + + static void setUpKotlinRuntimeLibrary( + @NotNull final Module module, + @NotNull final Library library, + @NotNull final Runnable afterSetUp + ) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + ModifiableRootModel model = ModuleRootManager.getInstance(module).getModifiableModel(); + if (model.findLibraryOrderEntry(library) == null) { + model.addLibraryEntry(library); + model.commit(); + } + else { + model.dispose(); + } + + afterSetUp.run(); + + if (!jdkAnnotationsArePresent(module)) { + addJdkAnnotations(module); + } + } + }); + } + + @Nullable + static Library findOrCreateRuntimeLibrary(@NotNull Project project, @NotNull FindRuntimeLibraryHandler handler) { + LibraryTable table = ProjectLibraryTable.getInstance(project); + Library kotlinRuntime = table.getLibraryByName(LIBRARY_NAME); + if (kotlinRuntime != null) { + for (VirtualFile root : kotlinRuntime.getFiles(OrderRootType.CLASSES)) { + if (root.getName().equals(KOTLIN_RUNTIME_JAR)) { + return kotlinRuntime; + } + } + } + + File runtimePath = PathUtil.getKotlinPathsForIdeaPlugin().getRuntimePath(); + if (!runtimePath.exists()) { + handler.runtimePathDoesNotExist(runtimePath); + return null; + } + + final File targetJar = handler.getRuntimeJarPath(); + if (targetJar == null) return null; + try { + FileUtil.copy(runtimePath, targetJar); + VirtualFile jarVfs = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(targetJar); + if (jarVfs != null) { + jarVfs.refresh(false, false); + } + } + catch (IOException e) { + handler.ioExceptionOnCopyingJar(e); + return null; + } + + if (kotlinRuntime == null) { + kotlinRuntime = table.createLibrary("KotlinRuntime"); + } + + final Library finalKotlinRuntime = kotlinRuntime; + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + Library.ModifiableModel model = finalKotlinRuntime.getModifiableModel(); + model.addRoot(VfsUtil.getUrlForLibraryRoot(targetJar), OrderRootType.CLASSES); + model.addRoot(VfsUtil.getUrlForLibraryRoot(targetJar) + "src", OrderRootType.SOURCES); + model.commit(); + } + }); + + return kotlinRuntime; + } + + public static void updateRuntime( + @NotNull final Project project, + @NotNull final Runnable jarNotFoundHandler + ) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + File runtimePath = PathUtil.getKotlinPathsForIdeaPlugin().getRuntimePath(); + if (!runtimePath.exists()) { + jarNotFoundHandler.run(); + return; + } + VirtualFile runtimeJar = getKotlinRuntimeJar(project); + assert runtimeJar != null; + VirtualFile jarFile = JarFileSystem.getInstance().getVirtualFileForJar(runtimeJar); + if (jarFile != null) { + runtimeJar = jarFile; + } + + try { + FileUtil.copy(runtimePath, new File(runtimeJar.getPath())); + } + catch (IOException e) { + throw new AssertionError(e); + } + runtimeJar.refresh(true, true); + } + }); + } + + @Nullable + public static String getRuntimeVersion(@NotNull Project project) { + VirtualFile kotlinRuntimeJar = getKotlinRuntimeJar(project); + if (kotlinRuntimeJar == null) return null; + VirtualFile manifestFile = kotlinRuntimeJar.findFileByRelativePath(JarFile.MANIFEST_NAME); + if (manifestFile != null) { + Attributes attributes = ManifestFileUtil.readManifest(manifestFile).getMainAttributes(); + if (attributes.containsKey(Attributes.Name.IMPLEMENTATION_VERSION)) { + return attributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION); + } + } + return UNKNOWN_VERSION; + } + + @Nullable + private static VirtualFile getKotlinRuntimeJar(@NotNull Project project) { + LibraryTable table = ProjectLibraryTable.getInstance(project); + Library kotlinRuntime = table.getLibraryByName(LIBRARY_NAME); + if (kotlinRuntime != null) { + for (VirtualFile root : kotlinRuntime.getFiles(OrderRootType.CLASSES)) { + if (root.getName().equals(KOTLIN_RUNTIME_JAR)) { + return root; + } + } + } + return null; + } + + public static abstract class FindRuntimeLibraryHandler { + @Nullable + public abstract File getRuntimeJarPath(); + + public void runtimePathDoesNotExist(@NotNull File path) { + } + + public void ioExceptionOnCopyingJar(@NotNull IOException e) { + } + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/versions/OutdatedKotlinRuntimeNotification.java b/idea/src/org/jetbrains/jet/plugin/versions/OutdatedKotlinRuntimeNotification.java index 28899ca2580..bbbf99aa2ca 100644 --- a/idea/src/org/jetbrains/jet/plugin/versions/OutdatedKotlinRuntimeNotification.java +++ b/idea/src/org/jetbrains/jet/plugin/versions/OutdatedKotlinRuntimeNotification.java @@ -14,8 +14,6 @@ package org.jetbrains.jet.plugin.versions;/* * limitations under the License. */ -import com.intellij.ide.plugins.IdeaPluginDescriptor; -import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.util.PropertiesComponent; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; @@ -23,31 +21,16 @@ import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.AbstractProjectComponent; -import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.OrderRootType; -import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; -import com.intellij.openapi.roots.libraries.Library; -import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.JarFileSystem; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.packaging.impl.elements.ManifestFileUtil; import com.intellij.util.text.VersionComparatorUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.plugin.quickfix.ConfigureKotlinLibraryNotificationProvider; -import org.jetbrains.jet.utils.PathUtil; +import org.jetbrains.jet.plugin.JetPluginUtil; +import org.jetbrains.jet.plugin.quickfix.KotlinRuntimeLibraryUtil; import javax.swing.event.HyperlinkEvent; -import java.io.File; -import java.io.IOException; -import java.util.jar.Attributes; -import java.util.jar.JarFile; public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent { - private static final String UNKNOWN_VERSION = "UNKNOWN"; private static final String SUPPRESSED_PROPERTY_NAME = "oudtdated.runtime.suppressed.plugin.version"; public OutdatedKotlinRuntimeNotification(final Project project) { @@ -57,8 +40,8 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent @Override public void projectOpened() { if (ApplicationManager.getApplication().isInternal()) return; - String runtimeVersion = getRuntimeVersion(); - final String pluginVersion = getPluginVersion(); + String runtimeVersion = KotlinRuntimeLibraryUtil.getRuntimeVersion(myProject); + final String pluginVersion = JetPluginUtil.getPluginVersion(); if (runtimeVersion == null) return; // runtime is not present in project if ("@snapshot@".equals(pluginVersion)) return; // plugin is run from sources, can't compare versions @@ -66,7 +49,7 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent if (pluginVersion.equals(PropertiesComponent.getInstance(myProject).getValue(SUPPRESSED_PROPERTY_NAME))) return; boolean isRuntimeOutdated = "snapshot".equals(runtimeVersion) - || UNKNOWN_VERSION.equals(runtimeVersion) + || KotlinRuntimeLibraryUtil.UNKNOWN_VERSION.equals(runtimeVersion) || runtimeVersion.startsWith("internal-") != pluginVersion.startsWith("internal-") || VersionComparatorUtil.compare(pluginVersion, runtimeVersion) > 0; @@ -75,7 +58,7 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent String message = String.format("

Your version of Kotlin runtime library is %s, while plugin version is %s." + " Runtime library should be updated to avoid compatibility problems.

" + "

Update Runtime Ignore

", - UNKNOWN_VERSION.equals(runtimeVersion) ? "older than 0.1.2296" : runtimeVersion, pluginVersion); + KotlinRuntimeLibraryUtil.UNKNOWN_VERSION.equals(runtimeVersion) ? "older than 0.1.2296" : runtimeVersion, pluginVersion); Notifications.Bus.notify(new Notification("Outdated Kotlin Runtime", "Outdated Kotlin Runtime", message, NotificationType.WARNING, new NotificationListener() { @@ -83,7 +66,14 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { if ("update".equals(event.getDescription())) { - updateRuntime(); + KotlinRuntimeLibraryUtil.updateRuntime(myProject, new Runnable() { + @Override + public void run() { + Messages.showErrorDialog(myProject, + "kotlin-runtime.jar is not found. Make sure plugin is properly installed.", + "No Runtime Found"); + } + }); } else if ("ignore".equals(event.getDescription())) { PropertiesComponent.getInstance(myProject).setValue(SUPPRESSED_PROPERTY_NAME, pluginVersion); @@ -95,68 +85,5 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent } } }), myProject); -} - - private void updateRuntime() { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - File runtimePath = PathUtil.getKotlinPathsForIdeaPlugin().getRuntimePath(); - if (!runtimePath.exists()) { - Messages.showErrorDialog(myProject, "kotlin-runtime.jar is not found. Make sure plugin is properly installed.", - "No Runtime Found"); - return; - } - VirtualFile runtimeJar = getKotlinRuntimeJar(); - assert runtimeJar != null; - VirtualFile jarFile = JarFileSystem.getInstance().getVirtualFileForJar(runtimeJar); - if (jarFile != null) { - runtimeJar = jarFile; - } - - try { - FileUtil.copy(runtimePath, new File(runtimeJar.getPath())); - } - catch (IOException e) { - throw new AssertionError(e); - } - runtimeJar.refresh(true, true); - } - }); - } - - @Nullable - private String getRuntimeVersion() { - VirtualFile kotlinRuntimeJar = getKotlinRuntimeJar(); - if (kotlinRuntimeJar == null) return null; - VirtualFile manifestFile = kotlinRuntimeJar.findFileByRelativePath(JarFile.MANIFEST_NAME); - if (manifestFile != null) { - Attributes attributes = ManifestFileUtil.readManifest(manifestFile).getMainAttributes(); - if (attributes.containsKey(Attributes.Name.IMPLEMENTATION_VERSION)) { - return attributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION); - } - } - return UNKNOWN_VERSION; - } - - @Nullable - private VirtualFile getKotlinRuntimeJar() { - LibraryTable table = ProjectLibraryTable.getInstance(myProject); - Library kotlinRuntime = table.getLibraryByName(ConfigureKotlinLibraryNotificationProvider.LIBRARY_NAME); - if (kotlinRuntime != null) { - for (VirtualFile root : kotlinRuntime.getFiles(OrderRootType.CLASSES)) { - if (root.getName().equals(ConfigureKotlinLibraryNotificationProvider.KOTLIN_RUNTIME_JAR)) { - return root; - } - } - } - return null; - } - - @NotNull - private static String getPluginVersion() { - IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId("org.jetbrains.kotlin")); - assert plugin != null : "How can it be? Kotlin plugin is available, but its component is running. Complete nonsense."; - return plugin.getVersion(); } }