
de.is24.deadcode4j.analyzer.AnnotationsAnalyzer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of deadcode4j-maven-plugin Show documentation
Show all versions of deadcode4j-maven-plugin Show documentation
Finds unused classes of a project
package de.is24.deadcode4j.analyzer;
import de.is24.deadcode4j.AnalysisContext;
import de.is24.deadcode4j.analyzer.javassist.ClassPathFilter;
import de.is24.guava.NonNullFunction;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.bytecode.annotation.Annotation;
import javax.annotation.Nonnull;
import java.lang.annotation.Inherited;
import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;
import static de.is24.deadcode4j.analyzer.javassist.ClassPoolAccessor.classPoolAccessorFor;
import static de.is24.javassist.CtClasses.getCtClass;
import static de.is24.javassist.CtClasses.getSuperclassOf;
import static de.is24.javassist.CtClasses.isJavaLangObject;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
import static java.util.Collections.disjoint;
import static java.util.Collections.emptySet;
/**
* Serves as a base class with which to mark classes as being in use if they carry one of the specified annotations.
*
* @since 1.3
*/
@SuppressWarnings("PMD.TooManyStaticImports")
public abstract class AnnotationsAnalyzer extends ByteCodeAnalyzer {
private static final Set DEAD_ENDS = newHashSet(
"java.lang.annotation.Documented",
"java.lang.annotation.Inherited",
"java.lang.annotation.Retention",
"java.lang.annotation.Target");
private final String dependerId;
private final NonNullFunction> supplyAnnotationsFoundInClassPath;
private final NonNullFunction> supplyAnnotationsMarkedAsInherited = new NonNullFunction>() {
@Nonnull
@Override
public List apply(@Nonnull AnalysisContext analysisContext) {
List inheritedAnnotations = newArrayList();
ClassPool classPool = classPoolAccessorFor(analysisContext).getClassPool();
for (String annotation : getAnnotationsFoundInClassPath(analysisContext)) {
CtClass annotationClazz = classPool.getOrNull(annotation);
if (annotationClazz == null) {
logger.debug("Annotation [{}] cannot be found on the class path; skipping detection", annotation);
continue;
}
try {
if (annotationClazz.getAnnotation(Inherited.class) != null) {
inheritedAnnotations.add(annotation);
}
} catch (ClassNotFoundException e) {
logger.debug("@Inherited is not available; this is quite disturbing.");
}
}
logger.debug("Found those inheritable annotations: {}", inheritedAnnotations);
return inheritedAnnotations;
}
};
private AnnotationsAnalyzer(@Nonnull String dependerId, @Nonnull Set annotations) {
checkArgument(!annotations.isEmpty(), "annotations cannot by empty!");
this.dependerId = dependerId;
this.supplyAnnotationsFoundInClassPath = new ClassPathFilter(annotations);
}
/**
* Creates a new AnnotationsAnalyzer
.
*
* @param dependerId a description of the depending entity with which to
* call {@link de.is24.deadcode4j.AnalysisContext#addDependencies(String, Iterable)}
* @param annotations a list of fully qualified (annotation) class names indicating a class is still in use
* @since 1.3
*/
protected AnnotationsAnalyzer(@Nonnull String dependerId, @Nonnull Iterable annotations) {
this(dependerId, newHashSet(annotations));
}
/**
* Creates a new AnnotationsAnalyzer
.
*
* @param dependerId a description of the depending entity with which to
* call {@link de.is24.deadcode4j.AnalysisContext#addDependencies(String, Iterable)}
* @param annotations a list of fully qualified (annotation) class names indicating a class is still in use
* @since 1.4
*/
protected AnnotationsAnalyzer(@Nonnull String dependerId, @Nonnull String... annotations) {
this(dependerId, newHashSet(annotations));
}
@Override
protected final void analyzeClass(@Nonnull AnalysisContext analysisContext, @Nonnull CtClass clazz) {
Set availableAnnotations = getAnnotationsFoundInClassPath(analysisContext);
if (availableAnnotations.isEmpty()) {
return;
}
String className = clazz.getName();
analysisContext.addAnalyzedClass(className);
Set allAnnotations = newHashSet();
addAnnotations(clazz, allAnnotations);
allAnnotations.addAll(getInheritedAnnotations(analysisContext, clazz));
if (!disjoint(availableAnnotations, allAnnotations)) {
analysisContext.addDependencies(this.dependerId, className);
}
}
private void addAnnotations(@Nonnull CtClass clazz, Set knownAnnotations) {
for (Annotation annotation : getAnnotations(clazz, PACKAGE, TYPE)) {
String annotationClassName = annotation.getTypeName();
if (!knownAnnotations.add(annotationClassName))
continue;
if (DEAD_ENDS.contains(annotationClassName))
continue;
CtClass annotationClazz = getCtClass(clazz.getClassPool(), annotationClassName);
if (annotationClazz != null) {
addAnnotations(annotationClazz, knownAnnotations);
}
}
}
@Nonnull
private Set getInheritedAnnotations(@Nonnull final AnalysisContext analysisContext, @Nonnull final CtClass clazz) {
List annotationsMarkedAsInherited = getAnnotationsMarkedAsInherited(analysisContext);
if (annotationsMarkedAsInherited.isEmpty()) {
return emptySet();
}
Set inheritedAnnotations = newHashSet();
CtClass loopClass = getSuperclassOf(clazz);
while (loopClass != null && !isJavaLangObject(loopClass)) {
for (Annotation annotation : getAnnotations(loopClass, PACKAGE, TYPE)) {
inheritedAnnotations.add(annotation.getTypeName());
}
loopClass = getSuperclassOf(loopClass);
}
return inheritedAnnotations;
}
@Nonnull
protected final Set getAnnotationsFoundInClassPath(@Nonnull AnalysisContext analysisContext) {
return analysisContext.getOrCreateCacheEntry(getClass().getName() + "|knownAnnotations", supplyAnnotationsFoundInClassPath);
}
@Nonnull
private List getAnnotationsMarkedAsInherited(@Nonnull AnalysisContext analysisContext) {
return analysisContext.getOrCreateCacheEntry(getClass().getName() + "|inheritableAnnotations", supplyAnnotationsMarkedAsInherited);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy