package org.netbeans.gradle.model.api; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import org.gradle.tooling.BuildAction; import org.netbeans.gradle.model.util.ClassLoaderUtils; import org.netbeans.gradle.model.util.CollectionUtils; /** * Defines the class path required by a particular {@link GradleInfoQuery}. * * @see GradleProjectInfoQuery */ public final class ModelClassPathDef { /** * A {@code ModelClassPathDef} instance containing no classpath entry and * the class loader used to load {@code ModelClassPathDef}. */ public static final ModelClassPathDef EMPTY = new ModelClassPathDef(ModelClassPathDef.class.getClassLoader()); private static final Set<File> EXCLUDED_PATHS = excludedPaths(); private final ClassLoader classLoader; private final Set<File> jarFiles; private ModelClassPathDef(ClassLoader classLoader) { if (classLoader == null) throw new NullPointerException("classLoader"); this.classLoader = classLoader; this.jarFiles = Collections.emptySet(); } private ModelClassPathDef(ClassLoader classLoader, Collection<? extends File> jarFiles) { if (classLoader == null) throw new NullPointerException("classLoader"); this.classLoader = classLoader; this.jarFiles = safeCanonFiles(jarFiles); CollectionUtils.checkNoNullElements(this.jarFiles, "jarFiles"); } private static Set<File> excludedPaths() { return Collections.unmodifiableSet(new HashSet<File>(Arrays.<File>asList( ClassLoaderUtils.getLocationOfClassPath(), ClassLoaderUtils.findClassPathOfClass(BuildAction.class) ))); } /** * Determines if the given classpath is implicitly assumed and therefore * cannot be part of a {@code ModelClassPathDef} instance. * <P> * This method assumes that the given classpath is in a canonical form * (see {@link File#getCanonicalFile()}). * * @param classPath the classpath entry to be checked if it is implicitly * assumed. This argument cannot be {@code null}. * @return {@code true} if the given classpath entry is implicitly assumed, * and must not be explicitly specified by {@code ModelClassPathDef}, * {@code false} otherwise */ public static boolean isImplicitlyAssumed(File classPath) { if (classPath == null) throw new NullPointerException("classPath"); return EXCLUDED_PATHS.contains(classPath); } private static File safeCanonFile(File file) { File canonFile; try { canonFile = file.getCanonicalFile(); } catch (IOException ex) { canonFile = file; } if (isImplicitlyAssumed(canonFile)) { throw new IllegalArgumentException("The given classpath is assumed implicitly and cannot be added: " + file); } return canonFile; } private static Set<File> safeCanonFiles(Collection<? extends File> files) { Set<File> result = CollectionUtils.newLinkedHashSet(files.size()); for (File file: files) { result.add(safeCanonFile(file)); } return result; } /** * Creates a classpath from the given set of jar files (or other classpath * entries). * * @param classLoader the {@code ClassLoader} which is to be used to * deserialize models received from the associated {@link ProjectInfoBuilder}. * This argument cannot be {@code null}. * @param jarFiles the jar files to be needed to serialize / deserialize * the associated {@code GradleInfoQuery}. This argument cannot be * {@code null} and cannot contain {@code null} elements. * @return the classpath from the given set of jar files. This method * never returns {@code null}. * * @throws IllegalArgumentException thrown if the specified classpath contains * an entry which is implicitly assumed and therefore cannot be added * for performance reasons * * @see #isImplicitlyAssumed(File) */ public static ModelClassPathDef fromJarFiles(ClassLoader classLoader, Collection<? extends File> jarFiles) { return new ModelClassPathDef(classLoader, jarFiles); } /** * Creates a classpath from the given classes, so that those classes will be * on the returned classpath. * * @param classLoader the {@code ClassLoader} which is to be used to * deserialize models received from the associated {@link ProjectInfoBuilder}. * This argument cannot be {@code null}. * @param classes the classes from where the classpath is deduced for * the associated {@code GradleInfoQuery}. This argument cannot be * {@code null} and cannot contain {@code null} elements. * @return the classpath from the given set of types. This method * never returns {@code null}. * * @see #isImplicitlyAssumed(File) */ public static ModelClassPathDef fromClasses(ClassLoader classLoader, Collection<? extends Class<?>> classes) { Set<File> modelClassPath = new LinkedHashSet<File>(); for (Class<?> type: classes) { File classpath = getClassPathOfClass(type); if (!isImplicitlyAssumed(classpath)) { modelClassPath.add(classpath); } } if (modelClassPath.isEmpty()) { return ModelClassPathDef.EMPTY; } return ModelClassPathDef.fromJarFiles(classLoader, modelClassPath); } /** * Returns the classpath from which the given type were loaded. This is * usually the jar file containing the given class. * * @param type the class whose classpath is to be returned. This argument * cannot be {@code null}. * @return the classpath from which the given type were loaded. This method * never returns {@code null}. * * @throws IllegalArgumentException thrown if the given class was not loaded * from the default file system */ public static File getClassPathOfClass(Class<?> type) { return ClassLoaderUtils.findClassPathOfClass(type); } /** * Returns the {@code ClassLoader} used to deserialize models returned by * the associated {@link ProjectInfoBuilder}. * * @return the {@code ClassLoader} used to deserialize models returned by * the associated {@link ProjectInfoBuilder}. This method never returns * {@code null}. */ public ClassLoader getClassLoader() { return classLoader; } /** * Returns the set of jar files (or other classpath entries) defining the * classpath. * * @return the set of jar files (or other classpath entries) defining the * classpath. This method never returns {@code null} and the returned * set does not contain {@code null} elements. */ public Set<File> getJarFiles() { return jarFiles; } }