javarequirementstracer.TraceReporter Maven / Gradle / Ivy
/*
* Copyright 2008-2009 the original author or authors.
*
* 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.
*/
package javarequirementstracer;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* Implementation of {@link JavaRequirementsTracer#write(SortedMap)}.
*
* @author Ronald Koster
*/
final class TraceReporter {
private static final Logger LOGGER = Logger.getInstance(TraceReporter.class);
private final File reportFile;
private final TraceProperties properties;
private final SortedMap> codeNamesToLabels;
private final SortedSet untraceableTypes;
private final SortedMap> labelsToCodeNames = new TreeMap>();
private final SortedSet missingLabels = new TreeSet();
private final SortedSet unknownLabels = new TreeSet();
TraceReporter(final TraceProperties properties, final SortedMap> codeNamesToLabels,
final SortedSet untraceableTypes) {
this.properties = properties;
this.reportFile = new File(this.properties.getReportFilename());
this.codeNamesToLabels = codeNamesToLabels;
this.untraceableTypes = untraceableTypes;
LOGGER.info("*** Report parameters: ");
LOGGER.info("reportFile = " + this.reportFile.getAbsolutePath());
LOGGER.debug("codeNamesToLabelsCount = " + this.codeNamesToLabels.size());
LOGGER.debug("untraceableTypesCount = " + this.untraceableTypes.size());
}
void run() {
initRun();
final XhtmlBuilder bldr = new XhtmlBuilder();
bldr.start("Traceabilities for " + this.properties.getProjectName());
ReporterUtils.appendReporterInfo(bldr);
appendSummary(bldr);
appendSettings(bldr);
appendMissingUnknownUntraceable(bldr);
appendMappingSection(bldr, AttributeId.REQUIREMENT_TO_CODE_ELEMENTS, this.labelsToCodeNames,
"Requirement", "Code Elements");
appendMappingSection(bldr, AttributeId.CODE_ELEMENT_TO_REQUIREMENTS, this.codeNamesToLabels,
"Code Element", "Requirements");
appendRequirementsDescriptions(bldr);
bldr.end();
bldr.write(this.reportFile);
}
private void initRun() {
// Map the other way around.
for (String codeName : this.codeNamesToLabels.keySet()) {
Set labels = this.codeNamesToLabels.get(codeName);
for (String label : labels) {
SortedSet codeNames = this.labelsToCodeNames.get(label);
if (codeNames == null) {
codeNames = new TreeSet();
this.labelsToCodeNames.put(label, codeNames);
}
codeNames.add(codeName);
}
}
// Gather all missing labels.
for (String label : this.properties.getExpectedLabels().keySet()) {
if (!this.labelsToCodeNames.containsKey(label)) {
this.missingLabels.add(label);
}
}
// Gather all unknown labels.
for (String label : this.labelsToCodeNames.keySet()) {
if (!this.properties.getExpectedLabels().containsKey(label)) {
this.unknownLabels.add(label);
}
}
}
private void appendSummary(XhtmlBuilder bldr) {
bldr.heading(2, "Summary");
ReporterUtils.appendTimestampBuildNumber(bldr, this.properties.getBuildNumber());
// Gather all traceable types.
final SortedSet traceableTypes = new TreeSet();
for (String codeName : this.codeNamesToLabels.keySet()) {
final int index = codeName.indexOf(RequirementsScanner.METHOD_SEPARATOR);
if (index < 0) {
// It is a type.
traceableTypes.add(codeName);
} else {
// It is a method.
traceableTypes.add(codeName.substring(0, index));
}
}
// Statistics.
final Collection> statistics = new ArrayList>();
final int traceableTypeCount = traceableTypes.size();
final int untraceableTypeCount = this.untraceableTypes.size();
final int allTypesCount = traceableTypeCount + untraceableTypeCount;
final float codeCoverage = getFraction(traceableTypeCount, allTypesCount);
statistics.add(Arrays.asList("CodeCoverage",
span(ReporterUtils.formatPercentage(codeCoverage), AttributeId.CODE_COVERAGE),
ReporterUtils.getPercentageGraph(codeCoverage),
span(ReporterUtils.CODE_COVERAGE_DEF + traceableTypeCount + "/" + allTypesCount,
AttributeId.CODE_COVERAGE_DESCRIPTION)));
final int foundLabelCount = this.labelsToCodeNames.size();
final int unknownLabelCount = this.unknownLabels.size();
final int requiredLabelCount = this.properties.getExpectedLabels().size();
final float requirementsCoverage = getFraction(foundLabelCount - unknownLabelCount, requiredLabelCount);
statistics.add(Arrays.asList("RequirementsCoverage",
span(ReporterUtils.formatPercentage(requirementsCoverage), AttributeId.REQUIREMENTS_COVERAGE),
ReporterUtils.getPercentageGraph(requirementsCoverage),
span(ReporterUtils.REQUIREMENTS_COVERAGE_DEF + (foundLabelCount - unknownLabelCount)
+ "/" + requiredLabelCount, AttributeId.REQUIREMENTS_COVERAGE_DESCRIPTION)));
bldr.table(AttributeId.COVERAGES, statistics);
// Checksums.
bldr.parStart().append("Checksums:");
appendChecksumLine(bldr, AttributeId.REQUIRED_LABEL_COUNT, "requiredLabelCount", requiredLabelCount,
"missingLabelCount + foundLabelCount - unknownLabelCount",
this.missingLabels.size() + " + " + foundLabelCount + " - " + unknownLabelCount,
this.missingLabels.size() + foundLabelCount - unknownLabelCount);
appendChecksumLine(bldr, AttributeId.ALL_TYPES_COUNT, "allTypesCount", allTypesCount,
"traceableTypeCount + untraceableTypeCount",
traceableTypeCount + " + " + untraceableTypeCount,
traceableTypeCount + untraceableTypeCount);
bldr.parEnd();
ReporterUtils.appendProgressIndicatorEstimate(bldr, false);
}
private void appendChecksumLine(XhtmlBuilder bldr, AttributeId id,
String quantityName, long quantityValue, String sumMsg, String sumValues, long sum) {
bldr.br().spanStart(id).append(quantityName).append(" = ").append(quantityValue)
.append(" =? ").append(sumMsg).append(" = ").append(sumValues).append(" = ").append(sum)
.tab().append("...").append(quantityValue == sum ? "OK" : "NOK!!!").spanEnd();
}
private float getFraction(int numerator, int denominator) {
float fnum = numerator;
float fdenom = denominator;
return fnum / fdenom;
}
private String span(String str, AttributeId attId) {
return "" + str + XhtmlBuilder.SPAN_END;
}
private void appendSettings(final XhtmlBuilder bldr) {
bldr.heading(2, "Settings");
bldr.parStart();
bldr.append("rootPackageName: ").append(AttributeId.ROOT_PACKAGE_NAME, this.properties.getRootPackageName());
bldr.br().append("includePackageNames: ").append(AttributeId.INCLUDE_PACKAGE_NAMES, this.properties.getIncludePackageNames());
bldr.br().append("excludePackageNames: ").append(AttributeId.EXCLUDE_PACKAGE_NAMES, this.properties.getExcludePackageNames());
bldr.br().append("excludeTypeNames: ").append(AttributeId.EXCLUDE_TYPE_NAMES, this.properties.getExcludeTypeNames());
bldr.br().append("includeTestCode: ").append(AttributeId.INCLUDE_TEST_CODE, this.properties.isIncludeTestCode());
bldr.parEnd();
}
private void appendMissingUnknownUntraceable(final XhtmlBuilder bldr) {
bldr.heading(2, "Missing, Unknown or Untraceable");
// Missing Requirements
appendSet(bldr, AttributeId.MISSING_REQUIREMENTS, this.missingLabels, "Missing Requirements");
// Unknown Requirements
bldr.br();
appendSet(bldr, AttributeId.UNKNOWN_REQUIREMENTS, this.unknownLabels, "Unknown Requirements");
// Untraceable Types
bldr.br();
appendSet(bldr, AttributeId.UNTRACEABLE_TYPES, this.untraceableTypes, "Untraceable Types");
}
private void appendSet(final XhtmlBuilder bldr, final AttributeId id, final SortedSet labels, final String header) {
final Collection> rows = new ArrayList>();
final Collection row = new ArrayList();
rows.add(row);
row.add(toString(labels));
bldr.table(id, rows, composeCountHeader(header, labels.size()));
}
private String toString(final SortedSet items) {
if (items.size() == 0) {
return "-";
}
final String str = items.toString();
return str.substring(1, str.length() - 1);
}
private void appendMappingSection(final XhtmlBuilder bldr,final AttributeId id,
final SortedMap> map, final String... headers) {
bldr.heading(2, headers[0] + " → " + headers[1]);
final SortedMap newMap = new TreeMap();
for (Map.Entry> entry : map.entrySet()) {
newMap.put(entry.getKey(), toString(entry.getValue()));
}
appendMapOfStrings(bldr, id, newMap, headers);
}
private void appendRequirementsDescriptions(final XhtmlBuilder bldr) {
bldr.heading(2, "Requirements Descriptions");
appendMapOfStrings(bldr, AttributeId.REQUIREMENT_DESCRIPTIONS, this.properties.getExpectedLabels(),
"Label", "Description");
}
private void appendMapOfStrings(final XhtmlBuilder bldr, final AttributeId id,
final SortedMap map, final String... headers) {
bldr.table(id, map, composeCountHeader(headers[0], map.size()), headers[1]);
}
private String composeCountHeader(String header, int count) {
return header + " (count" + XhtmlBuilder.NBSP + "=" + XhtmlBuilder.NBSP + count + ")";
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy