package de.is24.deadcode4j.analyzer; 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.Set; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Sets.newHashSet; import static de.is24.javassist.CtClasses.getAllImplementedInterfaces; import static java.util.Collections.disjoint; /** * Serves as a base class with which to mark classes as being in use if they explicitly implement one of the specified * interfaces. * * @since 1.4 */ public abstract class InterfacesAnalyzer extends ByteCodeAnalyzer { @Nonnull private final String dependerId; private final NonNullFunction<AnalysisContext, Set<String>> supplyInterfacesFoundInClassPath; private InterfacesAnalyzer(@Nonnull String dependerId, @Nonnull Set<String> interfaceNames) { checkArgument(!interfaceNames.isEmpty(), "interfaceNames cannot by empty!"); this.dependerId = dependerId; this.supplyInterfacesFoundInClassPath = new ClassPathFilter(interfaceNames); } /** * Creates a new <code>InterfacesAnalyzer</code>. * * @param dependerId a description of the <i>depending entity</i> with which to * call {@link de.is24.deadcode4j.AnalysisContext#addDependencies(String, Iterable)} * @param interfaceNames a list of fully qualified interface names indicating that the implementing class is still * in use * @since 1.4 */ protected InterfacesAnalyzer(@Nonnull String dependerId, @Nonnull String... interfaceNames) { this(dependerId, newHashSet(interfaceNames)); } /** * Creates a new <code>InterfacesAnalyzer</code>. * * @param dependerId a description of the <i>depending entity</i> with which to * call {@link de.is24.deadcode4j.AnalysisContext#addDependencies(String, Iterable)} * @param interfaceNames a list of fully qualified interface names indicating that the implementing class is still * in use * @since 1.4 */ protected InterfacesAnalyzer(@Nonnull String dependerId, @Nonnull Iterable<String> interfaceNames) { this(dependerId, newHashSet(interfaceNames)); } @Override protected final void analyzeClass(@Nonnull AnalysisContext analysisContext, @Nonnull CtClass clazz) { Set<String> knownInterfaces = getInterfacesFoundInClassPath(analysisContext); if (knownInterfaces.isEmpty()) { return; } String clazzName = clazz.getName(); analysisContext.addAnalyzedClass(clazzName); if (!disjoint(knownInterfaces, getAllImplementedInterfaces(clazz))) { analysisContext.addDependencies(this.dependerId, clazzName); } } @Nonnull protected final Set<String> getInterfacesFoundInClassPath(@Nonnull AnalysisContext analysisContext) { return analysisContext.getOrCreateCacheEntry(getClass(), supplyInterfacesFoundInClassPath); } }