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 com.google.common.collect.Ordering;
import org.mutabilitydetector.AnalysisError;
import org.mutabilitydetector.AnalysisResult;
import org.mutabilitydetector.IsImmutable;
import org.mutabilitydetector.MutableReasonDetail;
import org.mutabilitydetector.cli.CommandLineOptions.ReportMode;
import org.mutabilitydetector.locations.Dotted;
import org.mutabilitydetector.misc.TimingUtil;
import javax.annotation.concurrent.Immutable;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static org.mutabilitydetector.IsImmutable.IMMUTABLE;
import static org.mutabilitydetector.IsImmutable.NOT_IMMUTABLE;
@Immutable
public final class SessionResultsFormatter {
private final boolean verbose;
private final boolean showSummary;
private final ReportMode reportMode;
private final Collection<Dotted> classesToReport;
private final BatchAnalysisOptions options;
private TimingUtil timingUtil;
public SessionResultsFormatter(BatchAnalysisOptions options, ClassListReaderFactory readerFactory) {
this.options = options;
this.verbose = options.verbose();
this.showSummary = options.showSummary();
this.reportMode = options.reportMode();
this.classesToReport = getClassesToReport(options.isUsingClassList(), readerFactory);
this.timingUtil = new TimingUtil();
}
public StringBuilder format(Iterable<AnalysisResult> results, Iterable<AnalysisError> errors) {
StringBuilder output = new StringBuilder();
appendErrors(errors, output);
appendAnalysisResults(results, output);
return output;
}
private Collection<Dotted> getClassesToReport(boolean isUsingClassList, ClassListReaderFactory readerFactory) {
return isUsingClassList ? readerFactory.createReader().classListToReport() : Collections.<Dotted> emptySet();
}
private void appendErrors(Iterable<AnalysisError> errors, StringBuilder output) {
if (!options.reportErrors()) return;
for (AnalysisError error : errors) {
String message = String.format("Error while running %s on class %s.%n", error.checkerName, error.onClass);
output.append(message);
if (verbose) {
String description = String.format("\t%s%n", error.description);
output.append(description);
}
}
}
private void appendAnalysisResults(Iterable<AnalysisResult> results, StringBuilder output) {
List<AnalysisResult> sortedList = sortByClassname(results);
int total = 0;
int totalNotImmutable = 0;
for (AnalysisResult result : sortedList) {
IsImmutable isImmutable = result.isImmutable;
if (isImmutable.equals(NOT_IMMUTABLE)) {
totalNotImmutable++;
}
total++;
addResultForClass(output, result, isImmutable);
}
if (showSummary) {
int totalImmutable = total - totalNotImmutable;
appendSummaryOfResults(output, total, totalImmutable, totalNotImmutable);
}
}
private void appendSummaryOfResults(StringBuilder output, int total, int totalImmutable, int totalMutable) {
output.append(String.format("%n\t%d %s%n", total, "Total number of classes scanned."));
output.append(String.format("\t%d %s%n", totalImmutable, "IMMUTABLE class(es)."));
output.append(String.format("\t%d %s%n", totalMutable, "NOT_IMMUTABLE class(es)."));
final long processRuntime = timingUtil.getCurrentTimeMillis() - timingUtil.getVMStartTimeMillis();
output.append(String.format("\t%d %s%n", processRuntime/1000, "seconds runtime."));
}
private List<AnalysisResult> sortByClassname(Iterable<AnalysisResult> sessionResults) {
return Ordering.from(new ClassnameComparator()).sortedCopy(sessionResults);
}
private void addResultForClass(StringBuilder output, AnalysisResult result, IsImmutable isImmutable) {
if (options.isUsingClassList() && !classesToReport.contains(result.className)) return;
if (reportMode.equals(ReportMode.ALL)) {
appendClassResult(output, result, isImmutable);
} else if (reportMode.equals(ReportMode.IMMUTABLE)) {
if (result.isImmutable.equals(IMMUTABLE)) {
appendClassResult(output, result, isImmutable);
}
} else if (reportMode.equals(ReportMode.MUTABLE)) {
if (result.isImmutable.equals(NOT_IMMUTABLE)) {
appendClassResult(output, result, isImmutable);
}
}
}
private void appendClassResult(StringBuilder output, AnalysisResult result, IsImmutable isImmutable) {
output.append(String.format("%s is %s%n", result.className, isImmutable.name()));
if (!result.isImmutable.equals(IMMUTABLE)) {
addReasons(result, output);
}
}
private void addReasons(AnalysisResult result, StringBuilder output) {
if (!verbose) return;
for (MutableReasonDetail reasonDetail : result.reasons) {
output.append(String.format("\t%10s %s%n", reasonDetail.message(), reasonDetail.codeLocation()
.prettyPrint()));
}
}
private static final class ClassnameComparator implements Comparator<AnalysisResult>, Serializable {
private static final long serialVersionUID = 1865374158214841422L;
@Override
public int compare(AnalysisResult first, AnalysisResult second) {
return first.className.asString().compareToIgnoreCase(second.className.asString());
}
}
public void setTimingUtil(TimingUtil timingUtil) {
this.timingUtil = timingUtil;
}
}