
de.is24.deadcode4j.analyzer.javassist.ClassPoolAccessor 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.javassist;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.cache.LoadingCache;
import de.is24.deadcode4j.AnalysisContext;
import de.is24.deadcode4j.Repository;
import de.is24.guava.NonNullFunction;
import de.is24.guava.SequentialLoadingCache;
import javassist.ClassPool;
import javassist.NotFoundException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.util.Set;
import static com.google.common.base.Optional.absent;
import static com.google.common.base.Optional.of;
import static com.google.common.collect.Sets.newHashSet;
/**
* The ClassPoolAccessor
provides access to a Javassist {@link javassist.ClassPool} with fully configured
* class path. It also provides some convenience methods to deal with loading & resolving classes.
*
* @since 2.0.0
*/
public final class ClassPoolAccessor {
@Nonnull
private static final NonNullFunction SUPPLIER = new NonNullFunction() {
@Nonnull
@Override
public ClassPoolAccessor apply(@Nonnull AnalysisContext input) {
return new ClassPoolAccessor(input);
}
};
@Nonnull
private final ClassPool classPool;
@Nonnull
private final LoadingCache> classResolver;
public ClassPoolAccessor(@Nonnull AnalysisContext analysisContext) {
this.classPool = createClassPool(analysisContext);
this.classResolver = createResolverCache();
}
/**
* Creates or retrieves the ClassPoolAccessor
for the given analysis context.
* A new instance will be put in the analysis context's cache and subsequently retrieved from there.
*
* @since 2.0.0
*/
@Nonnull
public static ClassPoolAccessor classPoolAccessorFor(@Nonnull AnalysisContext analysisContext) {
return analysisContext.getOrCreateCacheEntry(ClassPoolAccessor.class, SUPPLIER);
}
@Nonnull
private static ClassPool createClassPool(AnalysisContext analysisContext) {
ClassPool classPool = new ClassPool(true);
try {
Repository outputRepository = analysisContext.getModule().getOutputRepository();
if (outputRepository != null) {
classPool.appendClassPath(outputRepository.getDirectory().getAbsolutePath());
}
for (File file : analysisContext.getModule().getClassPath()) {
classPool.appendClassPath(file.getAbsolutePath());
}
} catch (NotFoundException e) {
throw new RuntimeException("Failed to set up ClassPool!", e);
}
return classPool;
}
private static String prepareQualifier(CharSequence qualifier) {
String preparedQualifier = qualifier.toString();
for (; ; ) {
int lastDot = preparedQualifier.lastIndexOf('.');
int lastDollar = preparedQualifier.lastIndexOf('$');
if (lastDollar < 0 || lastDot < 0 || lastDollar > lastDot) {
break;
}
preparedQualifier = preparedQualifier.substring(0, lastDot) + "$" + preparedQualifier.substring(lastDot + 1);
}
return preparedQualifier;
}
/**
* Returns the ClassPool
used for examining classes.
*
* @since 2.0.0
*/
@Nonnull
public ClassPool getClassPool() {
return this.classPool;
}
/**
* Returns the "resolved" class name for the given qualifier.
* "Resolved" in this case means that if the qualifier refers to an existing class, the class'
* {@link java.lang.ClassLoader binary name} is returned.
*
* @since 2.0.0
*/
@Nonnull
public Optional resolveClass(@Nonnull CharSequence qualifier) {
return classResolver.getUnchecked(prepareQualifier(qualifier));
}
@Nonnull
private LoadingCache> createResolverCache() {
return new SequentialLoadingCache(new Function>() {
@Nonnull
private final Set knownPackages = newHashSet();
@Nullable
@Override
public Optional apply(@Nullable String input) {
if (input == null) {
return absent();
}
for (; ; ) {
if (classPool.getOrNull(input) != null) {
addToKnownPackages(input);
return of(input);
}
int dotIndex = input.lastIndexOf('.');
if (dotIndex < 0) {
return absent();
}
String potentialPackage = input.substring(0, dotIndex);
if (knownPackages.contains(potentialPackage)) {
// no need to look for inner classes
return absent();
}
input = potentialPackage + "$" + input.substring(dotIndex + 1);
}
}
private void addToKnownPackages(@Nonnull String className) {
for (; ; ) {
int dotIndex = className.lastIndexOf('.');
if (dotIndex < 0) {
return;
}
className = className.substring(0, dotIndex);
if (!knownPackages.add(className)) {
return;
}
}
}
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy