
de.is24.deadcode4j.DeadCodeFinder 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;
import org.apache.commons.io.DirectoryWalker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.IOException;
import java.util.*;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet;
import static de.is24.deadcode4j.Module.sort;
import static de.is24.deadcode4j.Utils.getOrAddMappedSet;
import static java.util.Arrays.asList;
/**
* The DeadCodeFinder
ties everything together in order to ultimately find dead code.
*
* @since 1.0.0
*/
@SuppressWarnings("PMD.TooManyStaticImports")
public class DeadCodeFinder {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Nonnull
private final DeadCodeComputer deadCodeComputer;
@Nonnull
private final Iterable extends Analyzer> analyzers;
public DeadCodeFinder(@Nonnull DeadCodeComputer deadCodeComputer, @Nonnull Set extends Analyzer> analyzers) {
this.deadCodeComputer = deadCodeComputer;
this.analyzers = newArrayList(analyzers);
}
@Nonnull
public DeadCode findDeadCode(@Nonnull Iterable modules) {
AnalyzedCode analyzedCode = analyzeCode(modules);
return computeDeadCode(analyzedCode);
}
@Nonnull
private AnalyzedCode analyzeCode(@Nonnull Iterable modules) {
List analyzedCode = newArrayList();
IntermediateResults intermediateResults = new IntermediateResults();
for (Module module : sort(modules)) {
AnalysisContext analysisContext = new AnalysisContext(module, intermediateResults.calculateIntermediateResultsFor(module));
for (Repository repository : module.getAllRepositories()) {
analyzeRepository(analysisContext, repository);
}
logger.debug("Finishing analysis of [{}]...", analysisContext);
for (Analyzer analyzer : this.analyzers) {
analyzer.finishAnalysis(analysisContext);
}
logger.debug("Finished analysis of [{}].", analysisContext);
intermediateResults.add(analysisContext);
analyzedCode.add(analysisContext.getAnalyzedCode());
}
logger.debug("Finishing analysis of whole project...");
AnalyzedCode combinedAnalysis = merge(analyzedCode);
for (Analyzer analyzer : this.analyzers) {
AnalysisSink analysisSink = new AnalysisSink();
analyzer.finishAnalysis(analysisSink, combinedAnalysis);
combinedAnalysis = merge(combinedAnalysis, analysisSink);
}
logger.debug("Finished analysis of project.");
return combinedAnalysis;
}
@Nonnull
private DeadCode computeDeadCode(@Nonnull AnalyzedCode analyzedCode) {
return this.deadCodeComputer.computeDeadCode(analyzedCode);
}
private void analyzeRepository(@Nonnull AnalysisContext analysisContext, @Nonnull Repository repository) {
RepositoryAnalyzer repositoryAnalyzer = new RepositoryAnalyzer(analysisContext, repository, this.analyzers);
try {
repositoryAnalyzer.analyze();
} catch (IOException e) {
throw new RuntimeException("This was unexpected; failed to parse files of " + repository + "!", e);
}
}
@Nonnull
private AnalyzedCode merge(@Nonnull List analyzedCode) {
EnumSet stagesWithExceptions = EnumSet.noneOf(AnalysisStage.class);
Set analyzedClasses = newHashSet();
Map> dependencies = newHashMap();
for (AnalyzedCode code : analyzedCode) {
stagesWithExceptions.addAll(code.getStagesWithExceptions());
analyzedClasses.addAll(code.getAnalyzedClasses());
for (Map.Entry> dependencyEntry : code.getCodeDependencies().entrySet()) {
Set knownDependencies = getOrAddMappedSet(dependencies, dependencyEntry.getKey());
knownDependencies.addAll(dependencyEntry.getValue());
}
}
return new AnalyzedCode(stagesWithExceptions, analyzedClasses, dependencies);
}
@Nonnull
private AnalyzedCode merge(@Nonnull AnalyzedCode analyzedCode, @Nonnull AnalysisSink analysisSink) {
AnalyzedCode analysisToAdd = analysisSink.getAnalyzedCode();
if (analysisToAdd.getStagesWithExceptions().isEmpty()
&& analysisToAdd.getAnalyzedClasses().isEmpty()
&& analysisToAdd.getCodeDependencies().isEmpty()) {
return analyzedCode;
}
return merge(asList(analyzedCode, analysisToAdd));
}
private static class RepositoryAnalyzer extends DirectoryWalker {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final AnalysisContext analysisContext;
private final Repository repository;
private final Iterable extends Analyzer> analyzers;
public RepositoryAnalyzer(@Nonnull AnalysisContext analysisContext, @Nonnull Repository repository, @Nonnull Iterable extends Analyzer> analyzers) {
super(repository.getFileFilter(), -1);
this.repository = repository;
this.analysisContext = analysisContext;
this.analyzers = analyzers;
}
public void analyze() throws IOException {
logger.debug("Starting analysis of [{}]...", this.repository);
super.walk(this.repository.getDirectory(), null);
}
@Override
protected void handleFile(File file, int depth, Collection results) {
logger.debug("Analyzing file [{}]...", file);
for (Analyzer analyzer : this.analyzers) {
try {
analyzer.doAnalysis(this.analysisContext, file);
} catch (RuntimeException rE) {
logger.warn("Analyzer [{}] failed to analyze file [{}]!", analyzer, file, rE);
analysisContext.addException(AnalysisStage.FILE_ANALYSIS);
}
}
}
@Override
protected void handleEnd(Collection results) {
logger.debug("Analysis of [{}] is done.", this.repository);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy