blob: 2b4b223502bccedc719098cc57f1038d617565c9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you 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.
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Read the file(s) given on the command line, which are presumed to be
* the output of the "check styles", and generate a summary of the results.
*/
public final class StyleErrors {
/** Private constructor because we only use static methods here. */
private StyleErrors() {
}
/**
* A summary object holding one type of error and the number of times
* it was encountered.
*/
private static class Info {
/** The error class from the check styles configuration. */
private String errorClass;
/** The final count of how many times this error was encountered. */
private Integer count;
/** Construct one of these with the given information.
* @param errClass The checkstyle error.
* @param c The final count of these errors.
*/
Info(final String errClass, final Integer c) {
this.errorClass = errClass;
this.count = c;
}
/** @return The saved checkstyle error name. */
String getErrorClass() {
return errorClass;
}
/** @return The final count of this error type. */
Integer getCount() {
return count;
}
}
/**
* A comparator that sorts first by count and then by the error class name.
*/
private static Comparator<Info> comparator = new Comparator<Info>() {
@Override
public int compare(final Info o1, final Info o2) {
// Order first by count, then by class name
int c1 = o1.count.intValue();
int c2 = o2.count.intValue();
if (c1 == c2) {
return o1.errorClass.compareTo(o2.errorClass);
} else {
return Integer.signum(c1 - c2);
}
}
};
/** Pattern used to parse each input line. */
private static final Pattern LINE_PATTERN =
Pattern.compile("^\\[[A-Z]+\\]\\s+(([a-zA-Z]\\:)?([^:]+))(\\:[0-9]+\\:)([0-9]+\\:)?\\s+(.+)\\s+(\\[[a-zA-Z]+\\])$");
/** The group in the {@link #LINE_PATTERN} that contains the file name. */
private static final int FILE_NAME_GROUP = 1;
/** The group in the {@link #LINE_PATTERN} that contains the checkstyle error name. */
private static final int CLASS_NAME_GROUP = 7;
/** A format string used to output all the information in a uniform manner. */
private static final String FORMAT = "%1$-32s%2$5d%n";
/** Format string used to print the underlines. */
private static final String UNDER_FORMAT = "%1$-32s%2$5s%n";
/** The set of unique file names found in the list. */
private static Set<String> fileNameSet = new HashSet<>();
/** For each type of checkstyle error, the name and running count for each. */
private static Map<String, Integer> counts = new TreeMap<>();
/** At the end of each file, the list used to sort by count and name. */
private static List<Info> sortedList = new ArrayList<>();
/**
* The main method, executed from the command line, which reads through each file
* and processes it.
* @param args The command line arguments.
*/
public static void main(final String[] args) {
for (String arg : args) {
int lineNo = 0;
counts.clear();
sortedList.clear();
try (BufferedReader reader = new BufferedReader(new FileReader(new File(arg)))) {
String line;
while ((line = reader.readLine()) != null) {
lineNo++;
Matcher m = LINE_PATTERN.matcher(line);
if (m.matches()) {
String fileName = m.group(FILE_NAME_GROUP);
fileNameSet.add(fileName);
String errorClass = m.group(CLASS_NAME_GROUP);
Integer count = counts.get(errorClass);
if (count == null) {
count = Integer.valueOf(1);
} else {
int i = count.intValue() + 1;
count = Integer.valueOf(i);
}
counts.put(errorClass, count);
} else if (line.equals("Starting audit...") || line.equals("Audit done.")) {
continue;
} else {
System.err.println("Line " + lineNo + ". Doesn't match the pattern.");
System.err.println("\t" + line);
}
}
} catch (IOException ioe) {
System.err.println("Error reading the \"" + arg + "\" file: " + ioe.getMessage());
}
int total = 0;
for (String key : counts.keySet()) {
Integer count = counts.get(key);
total += count.intValue();
Info info = new Info(key, count);
sortedList.add(info);
}
Collections.sort(sortedList, comparator);
for (Info info : sortedList) {
System.out.format(FORMAT, info.getErrorClass(), info.getCount());
}
System.out.format(UNDER_FORMAT, "----------------------------", "-----");
System.out.format(FORMAT, "Grand Total", total);
System.out.format(FORMAT, "Total Files With Errors", fileNameSet.size());
System.out.println();
}
}
}