package com.vaadin.tests.server;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Allows to get classes from the current classpath using classes FQN filter.
* <p>
* The methods in the class return all real (not anonymous and not private)
* classes from the filtered classpath.
*
* @author Vaadin Ltd
*
*/
public class ClasspathHelper {
public static final String COM_VAADIN_FILE_PREFIX = "com"
+ File.separatorChar + "vaadin" + File.separatorChar;
private final Predicate<String> skipClassesFilter;
public ClasspathHelper(Predicate<String> skipClassesFilter) {
this.skipClassesFilter = skipClassesFilter;
}
public ClasspathHelper() {
this(fqn -> false);
}
public Stream<Class<?>> getVaadinClassesFromClasspath(
Predicate<String> classpathFilter,
Predicate<Class<?>> classFilter) {
return getRawClasspathEntries().stream().filter(classpathFilter)
.map(File::new).map(file -> getVaadinClassesFromFile(file))
.flatMap(List::stream).filter(classFilter)
.filter(cls -> !cls.isSynthetic() && !cls.isAnonymousClass()
&& !Modifier.isPrivate(cls.getModifiers()));
}
public Stream<Class<?>> getVaadinClassesFromClasspath(
Predicate<String> classpathFilter) {
return getVaadinClassesFromClasspath(classpathFilter, cls -> true);
}
private List<Class<?>> getVaadinClassesFromFile(File classesRoot) {
try {
if (classesRoot.isDirectory()) {
return Files.walk(classesRoot.toPath())
.filter(Files::isRegularFile)
.filter(path -> path.toFile().getName()
.endsWith(".class"))
.filter(path -> classesRoot.toPath().relativize(path)
.toString().contains(COM_VAADIN_FILE_PREFIX))
.map(path -> getClassFromFile(path,
classesRoot.toPath()))
.filter(Objects::nonNull).collect(Collectors.toList());
} else if (classesRoot.getName().toLowerCase(Locale.ENGLISH)
.endsWith(".jar")) {
URI uri = URI.create("jar:" + classesRoot.toURI());
FileSystem fileSystem;
try {
fileSystem = FileSystems.getFileSystem(uri);
} catch (FileSystemNotFoundException e) {
fileSystem = null;
}
if (fileSystem == null) {
fileSystem = FileSystems.newFileSystem(uri,
Collections.emptyMap());
}
Path root = fileSystem.getPath(File.separator);
return Files.walk(root).filter(Files::isRegularFile)
.filter(path -> path.toUri().getSchemeSpecificPart()
.endsWith(".class"))
.filter(path -> root.relativize(path).toString()
.contains(COM_VAADIN_FILE_PREFIX))
.map(path -> getClassFromFile(path, root))
.filter(Objects::nonNull).collect(Collectors.toList());
}
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private Class<?> getClassFromFile(Path path, Path root) {
Path relative = root.relativize(path);
String name = relative.toString();
name = name.substring(0, name.length() - ".class".length());
name = name.replace(File.separatorChar, '.');
if (skipClassesFilter.test(name)) {
return null;
}
try {
return Class.forName(name, false, getClass().getClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
private final static List<String> getRawClasspathEntries() {
List<String> locations = new ArrayList<>();
String pathSep = System.getProperty("path.separator");
String classpath = System.getProperty("java.class.path");
if (classpath.startsWith("\"")) {
classpath = classpath.substring(1);
}
if (classpath.endsWith("\"")) {
classpath = classpath.substring(0, classpath.length() - 1);
}
String[] split = classpath.split(pathSep);
for (int i = 0; i < split.length; i++) {
String classpathEntry = split[i];
locations.add(classpathEntry);
}
return locations;
}
}