All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.is24.deadcode4j.analyzer.javassist.ClassPoolAccessor Maven / Gradle / Ivy

There is a newer version: 2.1.0
Show newest version
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