package org.jetbrains.plugins.clojure.config; import clojure.lang.AFn; import com.intellij.execution.configurations.JavaParameters; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.LibraryOrderEntry; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderEntry; 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.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayFactory; import com.intellij.util.ArrayUtil; import com.intellij.util.PathUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.clojure.ClojureBundle; import org.jetbrains.plugins.clojure.utils.LibrariesUtil; import java.io.File; import java.io.IOException; import java.util.List; import java.util.Properties; import java.util.jar.JarEntry; import java.util.jar.JarFile; import static org.jetbrains.plugins.clojure.utils.ClojureUtils.CLOJURE_NOTIFICATION_GROUP; /** * @author ilyas */ public class ClojureConfigUtil { public static final String CLOJURE_JAR_NAME_PREFIX = "clojure"; public static final String LIBRARY_PROPERTIES_PATH = "library.properties"; public static final String CLOJURE_MAIN_CLASS_FILE = "clojure/main.class"; public static final String VERSION_PROPERTY_KEY = "version.number"; public static final String UNDEFINED_VERSION = "undefined"; private static final Condition<Library> CLOJURE_LIB_CONDITION = new Condition<Library>() { public boolean value(Library library) { return isClojureLibrary(library); } }; public static String CLOJURE_SDK = PathUtil.getJarPathForClass(AFn.class); /** * Checks whether a given IDEA library contains Clojure Library classes */ public static boolean isClojureLibrary(Library library) { return library != null && checkLibrary(library, CLOJURE_JAR_NAME_PREFIX, CLOJURE_MAIN_CLASS_FILE); } static boolean checkLibrary(Library library, String jarNamePrefix, String necessaryClass) { boolean result = false; VirtualFile[] classFiles = library.getFiles(OrderRootType.CLASSES); for (VirtualFile file : classFiles) { String path = file.getPath(); if (path != null && "jar".equals(file.getExtension())) { path = StringUtil.trimEnd(path, "!/"); String name = file.getName(); File realFile = new File(path); if (realFile.exists()) { try { JarFile jarFile = new JarFile(realFile); if (name.startsWith(jarNamePrefix)) { result = jarFile.getJarEntry(necessaryClass) != null; } jarFile.close(); } catch (IOException e) { result = false; } } } } return result; } private static String getClojureVersion(@NotNull String jarPath) { String jarVersion = getClojureJarVersion(jarPath, LIBRARY_PROPERTIES_PATH); return jarVersion != null ? jarVersion : UNDEFINED_VERSION; } /** * Return value of Implementation-Version attribute in jar manifest * <p/> * * @param jarPath path to jar file * @param propPath path to properties file in jar file * @return value of Implementation-Version attribute, null if not found */ public static String getClojureJarVersion(String jarPath, String propPath) { try { File file = new File(jarPath); if (!file.exists()) { return null; } JarFile jarFile = new JarFile(file); JarEntry jarEntry = jarFile.getJarEntry(propPath); if (jarEntry == null) { return null; } Properties properties = new Properties(); properties.load(jarFile.getInputStream(jarEntry)); String version = properties.getProperty(VERSION_PROPERTY_KEY); jarFile.close(); return version; } catch (Exception e) { return null; } } public static Library[] getProjectClojureLibraries(Project project) { if (project == null) return new Library[0]; final LibraryTable table = ProjectLibraryTable.getInstance(project); final List<Library> all = ContainerUtil.findAll(table.getLibraries(), CLOJURE_LIB_CONDITION); return all.toArray(new Library[all.size()]); } public static Library[] getAllClojureLibraries(@Nullable Project project) { return ArrayUtil.mergeArrays(getGlobalClojureLibraries(), getProjectClojureLibraries(project), new ArrayFactory<Library>() { @NotNull public Library[] create(int count) { return new Library[count]; } }); } public static Library[] getGlobalClojureLibraries() { return LibrariesUtil.getGlobalLibraries(CLOJURE_LIB_CONDITION); } static String getSpecificJarForLibrary(Library library, String jarNamePrefix, String necessaryClass) { VirtualFile[] classFiles = library.getFiles(OrderRootType.CLASSES); for (VirtualFile file : classFiles) { String path = file.getPath(); if (path != null && "jar".equals(file.getExtension())) { path = StringUtil.trimEnd(path, "!/"); String name = file.getName(); File realFile = new File(path); if (realFile.exists()) { try { JarFile jarFile = new JarFile(realFile); if (name.startsWith(jarNamePrefix) && jarFile.getJarEntry(necessaryClass) != null) { return path; } jarFile.close(); } catch (IOException e) { //do nothing } } } } return ""; } public static Library[] getClojureSdkLibrariesByModule(final Module module) { return LibrariesUtil.getLibrariesByCondition(module, CLOJURE_LIB_CONDITION); } @NotNull public static String getClojureSdkJarPath(Module module) { if (module == null) return ""; Library[] libraries = getClojureSdkLibrariesByModule(module); if (libraries.length == 0) return ""; final Library library = libraries[0]; return getClojureJarPathForLibrary(library); } public static String getClojureJarPathForLibrary(Library library) { return getSpecificJarForLibrary(library, CLOJURE_JAR_NAME_PREFIX, CLOJURE_MAIN_CLASS_FILE); } public static boolean isClojureConfigured(final Module module) { ModuleRootManager manager = ModuleRootManager.getInstance(module); for (OrderEntry entry : manager.getOrderEntries()) { if (entry instanceof LibraryOrderEntry) { Library library = ((LibraryOrderEntry) entry).getLibrary(); if (library != null) { for (VirtualFile file : library.getFiles(OrderRootType.CLASSES)) { String path = file.getPath(); if (path.endsWith(".jar!/")) { if (file.findFileByRelativePath(CLOJURE_MAIN_CLASS_FILE) != null) { return true; } } } } } } return false; } public static void warningDefaultClojureJar(Module module) { Notifications.Bus.notify(new Notification(CLOJURE_NOTIFICATION_GROUP, "", ClojureBundle.message("clojure.jar.from.plugin.used"), NotificationType.WARNING), module.getProject()); } public static class RunConfigurationParameters extends JavaParameters { private boolean defaultClojureJarUsed = false; public boolean isDefaultClojureJarUsed() { return defaultClojureJarUsed; } public void setDefaultClojureJarUsed(boolean defaultClojureJarUsed) { this.defaultClojureJarUsed = defaultClojureJarUsed; } } }