package org.mutabilitydetector.cli; /* * #%L * MutabilityDetector * %% * Copyright (C) 2008 - 2014 Graham Allan * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import static com.google.classpath.RegExpResourceFilter.ANY; import static com.google.classpath.RegExpResourceFilter.ENDS_WITH_CLASS; import static org.mutabilitydetector.Configurations.OUT_OF_THE_BOX_CONFIGURATION; import static org.mutabilitydetector.DefaultCachingAnalysisSession.createWithGivenClassPath; import static org.mutabilitydetector.checkers.CheckerRunner.ExceptionPolicy.CARRY_ON; import static org.mutabilitydetector.checkers.CheckerRunner.ExceptionPolicy.FAIL_FAST; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import org.mutabilitydetector.AnalysisSession; import org.mutabilitydetector.Configuration; import org.mutabilitydetector.ConfigurationBuilder; import org.mutabilitydetector.asmoverride.AsmVerifierFactory; import org.mutabilitydetector.asmoverride.AsmVerifierFactory.ClassloadingOption; import org.mutabilitydetector.asmoverride.ClassLoadingVerifierFactory; import org.mutabilitydetector.asmoverride.NonClassLoadingVerifierFactory; import org.mutabilitydetector.checkers.ClassPathBasedCheckerRunnerFactory; import org.mutabilitydetector.checkers.MutabilityCheckerFactory; import org.mutabilitydetector.checkers.MutabilityCheckerFactory.ReassignedFieldAnalysisChoice; import org.mutabilitydetector.classloading.CachingAnalysisClassLoader; import org.mutabilitydetector.classloading.ClassForNameWrapper; import org.mutabilitydetector.locations.Dotted; import com.google.classpath.ClassPath; import com.google.classpath.ClassPathFactory; import com.google.classpath.RegExpResourceFilter; /** * Runs an analysis configured by the given classpath and options. * */ public final class RunMutabilityDetector implements Runnable, Callable<String> { private final ClassPath classpath; private final BatchAnalysisOptions options; private final NamesFromClassResources namesFromClassResources; public RunMutabilityDetector(ClassPath classpath, BatchAnalysisOptions options, NamesFromClassResources namesFromClassResources) { this.classpath = classpath; this.options = options; this.namesFromClassResources = namesFromClassResources; } /** * Runs mutability detection, printing the results to System.out. */ @Override public void run() { StringBuilder output = getResultString(); System.out.println(output); } /** * Runs mutability detection, returning the results as a String. */ @Override public String call() throws Exception { return getResultString().toString(); } private StringBuilder getResultString() { RegExpResourceFilter regExpResourceFilter = new RegExpResourceFilter(ANY, ENDS_WITH_CLASS); String[] findResources = classpath.findResources("", regExpResourceFilter); List<Dotted> filtered = namesFromClassResources.asDotted(findResources); Configuration configuration = new ConfigurationBuilder() { @Override public void configure() { mergeHardcodedResultsFrom(OUT_OF_THE_BOX_CONFIGURATION); setExceptionPolicy(options.failFast() ? FAIL_FAST : CARRY_ON); setClassloadingPolicy(options.classloading()); } }.build(); String[] classPathFiles = new ClassPathFactory().parseClasspath(options.classpath()); AsmVerifierFactory verifierFactory = options.classloading() == ClassloadingOption.ENABLED ? createClassLoadingVerifierFactory(classPathFiles) : new NonClassLoadingVerifierFactory(classpath); AnalysisSession newSession = createWithGivenClassPath(classpath, new ClassPathBasedCheckerRunnerFactory(classpath, configuration.exceptionPolicy()), new MutabilityCheckerFactory(ReassignedFieldAnalysisChoice.NAIVE_PUT_FIELD_ANALYSIS, configuration.immutableContainerClasses()), verifierFactory, configuration); AnalysisSession completedSession = new BatchAnalysisSession(newSession).runAnalysis(filtered); ClassListReaderFactory readerFactory = new ClassListReaderFactory(options.classListFile()); return new SessionResultsFormatter(options, readerFactory) .format(completedSession.getResults(), completedSession.getErrors()); } private ClassLoadingVerifierFactory createClassLoadingVerifierFactory(String[] classPathFiles) { return new ClassLoadingVerifierFactory( new CachingAnalysisClassLoader( new URLFallbackClassLoader(getCustomClassLoader(classPathFiles), new ClassForNameWrapper()))); } private URLClassLoader getCustomClassLoader(String[] classPathFiles) { List<URL> urlList = new ArrayList<>(classPathFiles.length); for (String classPathUrl : classPathFiles) { try { URL toAdd = new File(classPathUrl).toURI().toURL(); urlList.add(toAdd); } catch (MalformedURLException e) { System.err.printf("Classpath option %s is invalid.", classPathUrl); } } return new URLClassLoader(urlList.toArray(new URL[urlList.size()])); } public static void main(String[] args) { BatchAnalysisOptions options = createOptionsFromArgs(args); ClassPath classpath = new ClassPathFactory().createFromPath(options.classpath()); new RunMutabilityDetector(classpath, options, new NamesFromClassResources(options.match())).run(); } private static BatchAnalysisOptions createOptionsFromArgs(String[] args) { try { return new CommandLineOptions(System.err, args); } catch (Throwable e) { System.out.println("Exiting..."); System.exit(1); throw new IllegalStateException(); } } }