com.softicar.platform.common.code.classpath.metadata.ClasspathFilesMetadata Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of platform-common Show documentation
Show all versions of platform-common Show documentation
The SoftiCAR Platform is a lightweight, Java-based library to create interactive business web applications.
package com.softicar.platform.common.code.classpath.metadata;
import com.softicar.platform.common.core.exceptions.SofticarDeveloperException;
import com.softicar.platform.common.core.exceptions.SofticarIOException;
import com.softicar.platform.common.core.java.classpath.JavaClasspath;
import com.softicar.platform.common.io.classfile.ClassFile;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;
/**
* Identify classes that annotate, implement, or extend certain classes or
* interfaces
*/
public class ClasspathFilesMetadata implements IClasspathFilesMetadata {
private static final ClasspathFilesMetadata INSTANCE = new ClasspathFilesMetadata();
private static final String MODULE_INFO_CLASS_FILENAME = "module-info.class";
private final Map> extendingClasses;
private final Map> implementingClasses;
private final Map> annotatedClasses;
private ClasspathFilesMetadata() {
extendingClasses = new TreeMap<>();
implementingClasses = new TreeMap<>();
annotatedClasses = new TreeMap<>();
for (var root: JavaClasspath.getInstance().getPayloadRoots()) {
for (var file: root.getClassFiles()) {
if (!file.getName().equals(MODULE_INFO_CLASS_FILENAME)) {
try (InputStream inputStream = file.getInputStream()) {
ClassFile classFile = new ClassFile(inputStream);
parseMetadata(extendingClasses, classFile, Collections.singleton(classFile.getSuperClass()));
parseMetadata(implementingClasses, classFile, classFile.getInterfaces());
parseMetadata(annotatedClasses, classFile, parseAnnotationsFrom(classFile));
} catch (IOException exception) {
throw new SofticarIOException(exception);
}
}
}
}
}
public static ClasspathFilesMetadata getInstance() {
return INSTANCE;
}
@Override
public Collection> getDirectlyImplementingClasses(Class interfaceClass) {
return getClasses(interfaceClass, implementingClasses);
}
@Override
public Collection> getDirectlyExtendingClasses(Class baseClass) {
return getClasses(baseClass, extendingClasses);
}
@Override
public Collection> getDirectlyAnnotatedClasses(Class annotation) {
return getClasses(annotation, annotatedClasses);
}
@Override
public Collection> getTransitivelyImplementingClasses(Class interfaces) {
return new TransitivelyImplementingClassesFinder(this).findAll(interfaces);
}
@Override
public Collection> getTransitivelyExtendingClasses(Class baseClass) {
return new TransitivelyExtendingClassesFinder(this).findAll(baseClass);
}
// ------------------------------ private ------------------------------ //
private Collection> getClasses(Class interfaceClass, Map> classesMap) {
return classesMap//
.getOrDefault(interfaceClass.getName(), Collections.emptySet())
.stream()
.map(this::getClassByName)
.collect(Collectors.toList());
}
private Class getClassByName(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException exception) {
throw new SofticarDeveloperException(exception);
}
}
private void parseMetadata(Map> classMetadataMap, ClassFile classFile, Collection metadataRaw) {
for (String classname: metadataRaw) {
classMetadataMap//
.computeIfAbsent(classname.replace("/", "."), dummy -> new TreeSet<>())
.add(classFile.getThisClass().replace("/", "."));
}
}
private Collection parseAnnotationsFrom(ClassFile classFile) {
TreeSet annotations = new TreeSet<>();
classFile //
.getAttributes()
.stream()
.filter(
filteredAttribute -> filteredAttribute.getName().equals("RuntimeVisibleAnnotations")
|| filteredAttribute.getName().equals("RuntimeInvisibleAnnotations"))
.forEach(foundAttribute -> annotations.addAll(foundAttribute.parseAnnotationsFrom()));
return annotations;
}
}