package de.is24.deadcode4j.analyzer;
import com.google.common.collect.Sets;
import de.is24.deadcode4j.AnalysisContext;
import de.is24.deadcode4j.analyzer.javassist.ClassPathFilter;
import de.is24.guava.NonNullFunction;
import javassist.CtClass;
import javax.annotation.Nonnull;
import java.util.Collections;
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 de.is24.javassist.CtClasses.getSuperclassOf;
import static de.is24.javassist.CtClasses.isJavaLangObject;
/**
* Serves as a base class with which to mark classes as being in use if they are a direct subclass of one of the
* specified classes.
*
* @since 1.4
*/
public abstract class SuperClassAnalyzer extends ByteCodeAnalyzer {
private final String dependerId;
private final NonNullFunction<AnalysisContext, Set<String>> supplySuperClassesFoundInClassPath;
private SuperClassAnalyzer(@Nonnull String dependerId, @Nonnull Set<String> classNames) {
checkArgument(!classNames.isEmpty(), "classNames cannot by empty!");
this.dependerId = dependerId;
supplySuperClassesFoundInClassPath = new ClassPathFilter(classNames);
}
/**
* Creates a new <code>SuperClassAnalyzer</code>.
*
* @param dependerId a description of the <i>depending entity</i> with which to
* call {@link de.is24.deadcode4j.AnalysisContext#addDependencies(String, Iterable)}
* @param classNames a list of fully qualified class names indicating that the extending class is still in use
* @since 1.4
*/
protected SuperClassAnalyzer(@Nonnull String dependerId, @Nonnull String... classNames) {
this(dependerId, Sets.newHashSet(classNames));
}
/**
* Creates a new <code>SuperClassAnalyzer</code>.
*
* @param dependerId a description of the <i>depending entity</i> with which to
* call {@link de.is24.deadcode4j.AnalysisContext#addDependencies(String, Iterable)}
* @param classNames a list of fully qualified class names indicating that the extending class is still in use
* @since 1.4
*/
public SuperClassAnalyzer(String dependerId, Iterable<String> classNames) {
this(dependerId, Sets.newHashSet(classNames));
}
@Override
protected final void analyzeClass(@Nonnull AnalysisContext analysisContext, @Nonnull CtClass clazz) {
Set<String> knownSuperClasses = getSuperClassesFoundInClassPath(analysisContext);
if (knownSuperClasses.isEmpty()) {
return;
}
String clazzName = clazz.getName();
analysisContext.addAnalyzedClass(clazzName);
if (!Collections.disjoint(knownSuperClasses, getClassHierarchy(clazz))) {
analysisContext.addDependencies(this.dependerId, clazzName);
}
}
@Nonnull
private List<String> getClassHierarchy(@Nonnull final CtClass clazz) {
List<String> classes = newArrayList();
CtClass loopClass = clazz;
do {
classes.add(loopClass.getClassFile2().getSuperclass());
loopClass = getSuperclassOf(loopClass);
} while (loopClass != null && !isJavaLangObject(loopClass));
return classes;
}
@Nonnull
protected final Set<String> getSuperClassesFoundInClassPath(@Nonnull AnalysisContext analysisContext) {
return analysisContext.getOrCreateCacheEntry(getClass(), supplySuperClassesFoundInClassPath);
}
}