package org.netbeans.gradle.project.query; import java.io.File; import java.io.FilenameFilter; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.java.classpath.JavaClassPathConstants; import org.netbeans.api.java.platform.JavaPlatform; import org.netbeans.gradle.model.util.CollectionUtils; import org.netbeans.gradle.project.GradleHomeRegistry; import org.netbeans.gradle.project.properties.global.CommonGlobalSettings; import org.netbeans.gradle.project.util.GradleFileUtils; import org.netbeans.gradle.project.util.UrlFactory; import org.netbeans.spi.java.classpath.ClassPathProvider; import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProviders; @ServiceProviders({@ServiceProvider(service = ClassPathProvider.class)}) public final class GradleHomeClassPathProvider implements ClassPathProvider { private static final URL[] NO_URLS = new URL[0]; private final SimpleCache<FileObject, ClassPath> sourcePathsCache; private final SimpleCache<FileObject, ClassPath> binPathsCache; public GradleHomeClassPathProvider() { this.sourcePathsCache = new SimpleCache<>(1); this.binPathsCache = new SimpleCache<>(1); } private static File[] tryListFiles(File dir, FilenameFilter filter) { if (!dir.isDirectory()) { return null; } return dir.listFiles(filter); } private static List<File> getLibsFromGradleRoot(File gradleHome, FilenameFilter filter) { if (!gradleHome.isDirectory()) { return Collections.emptyList(); } File libDir = GradleFileUtils.getLibDirOfGradle(gradleHome); File[] jars = tryListFiles(libDir, filter); if (jars == null) { return Collections.emptyList(); } List<File> result = new ArrayList<>(Arrays.asList(jars)); File pluginsDir = new File(libDir, "plugins"); File[] pluginJars = tryListFiles(pluginsDir, filter); if (pluginJars != null) { result.addAll(Arrays.asList(pluginJars)); } return result; } public static URL[] getGradleLibs(FileObject gradleHomeObj, FilenameFilter filter) { File gradleHome = FileUtil.toFile(gradleHomeObj); if (gradleHome == null || !gradleHome.isDirectory()) { return NO_URLS; } List<File> jars = getLibsFromGradleRoot(gradleHome, filter); if (jars.isEmpty()) { return NO_URLS; } UrlFactory urlFactory = UrlFactory.getDefaultArchiveOrDirFactory(); List<URL> result = new ArrayList<>(jars.size()); for (File jar: jars) { URL url = urlFactory.toUrl(jar); if (url != null) { result.add(url); } } return result.toArray(NO_URLS); } public static URL[] getAllGradleLibs(FileObject gradleHomeObj) { return getGradleLibs(gradleHomeObj, new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.toLowerCase(Locale.US).endsWith(".jar"); } }); } public static URL[] getGradleBinaries(FileObject gradleHomeObj) { return getGradleLibs(gradleHomeObj, new FilenameFilter() { @Override public boolean accept(File dir, String name) { String lowerCaseName = name.toLowerCase(Locale.US); return lowerCaseName.startsWith("gradle-") && lowerCaseName.endsWith(".jar"); } }); } private ClassPath getSourcePaths(FileObject gradleHome, FileObject file) { FileObject srcDir = GradleFileUtils.getSrcDirOfGradle(gradleHome); if (srcDir == null || FileUtil.getRelativePath(srcDir, file) == null) { return null; } if (srcDir.isFolder()) { ClassPath classpath = sourcePathsCache.tryGetFromCache(gradleHome); if (classpath != null) { return classpath; } classpath = ClassPathSupport.createClassPath(srcDir); sourcePathsCache.addToCache(gradleHome, classpath); return classpath; } else { return null; } } private ClassPath getBinaryPaths(FileObject gradleHome) { ClassPath classpath = binPathsCache.tryGetFromCache(gradleHome); if (classpath != null) { return classpath; } URL[] libs = getAllGradleLibs(gradleHome); classpath = ClassPathSupport.createClassPath(libs); binPathsCache.addToCache(gradleHome, classpath); return classpath; } @Override public ClassPath findClassPath(FileObject file, String type) { if (type == null) { return null; } FileObject gradleHome = CommonGlobalSettings.getDefault().tryGetGradleInstallation(); if (gradleHome == null) { return null; } if (!FileUtil.isParentOf(gradleHome, file)) { return null; } GradleHomeRegistry.requireGradlePaths(); switch (type) { case ClassPath.SOURCE: return getSourcePaths(gradleHome, file); case ClassPath.BOOT: JavaPlatform platform = JavaPlatform.getDefault(); return platform != null ? platform.getBootstrapLibraries() : null; case ClassPath.COMPILE: return getBinaryPaths(gradleHome); case ClassPath.EXECUTE: return getBinaryPaths(gradleHome); case JavaClassPathConstants.PROCESSOR_PATH: return getBinaryPaths(gradleHome); default: return null; } } private static class SimpleCache<KeyType, ValueType> { private final Lock cacheLock; private final Map<KeyType, ValueType> cache; private final int maxCapacity; public SimpleCache(int maxCapacity) { this.cacheLock = new ReentrantLock(); this.cache = CollectionUtils.newLinkedHashMap(maxCapacity); this.maxCapacity = maxCapacity; } public void addToCache(KeyType key, ValueType value) { cacheLock.lock(); try { cache.put(key, value); while (cache.size() > maxCapacity) { Iterator<?> itr = cache.entrySet().iterator(); itr.next(); itr.remove(); } } finally { cacheLock.unlock(); } } public ValueType tryGetFromCache(KeyType key) { cacheLock.lock(); try { return cache.get(key); } finally { cacheLock.unlock(); } } } }