package com.sleekbyte.tailor.listeners;
import com.sleekbyte.tailor.common.ConstructLengths;
import com.sleekbyte.tailor.common.Location;
import com.sleekbyte.tailor.common.Messages;
import com.sleekbyte.tailor.common.Rules;
import com.sleekbyte.tailor.output.Printer;
import com.sleekbyte.tailor.utils.SourceFileUtil;
import java.io.File;
import java.io.IOException;
import java.io.LineNumberReader;
import java.nio.file.Files;
import java.util.Set;
/**
* Listener for verifying source files.
*/
public final class FileListener implements AutoCloseable {
private Printer printer;
private File inputFile;
private ConstructLengths constructLengths;
private LineNumberReader reader;
private int numOfLines = 0;
private Set<Rules> enabledRules;
/**
* Constructs a file listener with the specified printer, input file, and max lengths restrictions.
*
* @param printer the printer to use for displaying violation messages
* @param inputFile the source file to verify
* @param constructLengths the restrictions for maximum lengths
*/
public FileListener(Printer printer, File inputFile, ConstructLengths constructLengths, Set<Rules> enabledRules)
throws IOException {
this.printer = printer;
this.inputFile = inputFile;
this.constructLengths = constructLengths;
this.reader = new LineNumberReader(Files.newBufferedReader(inputFile.toPath()));
this.enabledRules = enabledRules;
}
@Override
public void close() throws IOException {
this.reader.close();
}
/**
* Verify that all source file specific rules are satisfied.
*/
public void verify() throws IOException {
int lineLength;
int lineNumber;
for (String line = this.reader.readLine(); line != null; line = this.reader.readLine()) {
lineLength = line.length();
lineNumber = this.reader.getLineNumber();
// Count the number of lines in a file
this.numOfLines++;
if (enabledRules.contains(Rules.MAX_LINE_LENGTH)
&& SourceFileUtil.lineTooLong(lineLength, this.constructLengths.maxLineLength)) {
lineLengthViolation(lineNumber, lineLength, this.constructLengths.maxLineLength, Rules.MAX_LINE_LENGTH,
Messages.EXCEEDS_CHARACTER_LIMIT);
}
if (enabledRules.contains(Rules.TRAILING_WHITESPACE)
&& SourceFileUtil.lineHasTrailingWhitespace(lineLength, line)) {
trailingWhitespaceViolation(lineNumber, lineLength);
}
}
if (enabledRules.contains(Rules.MAX_FILE_LENGTH)) {
verifyFileLength();
}
if (enabledRules.contains(Rules.TERMINATING_NEWLINE)) {
verifyNewlineTerminated();
}
if (enabledRules.contains(Rules.LEADING_WHITESPACE)) {
verifyNoLeadingWhitespace();
}
}
private void lineLengthViolation(int lineNumber, int lineLength, int lengthLimit, Rules rule, String msg) {
String lengthVersusLimit = " (" + lineLength + "/" + lengthLimit + ")";
// Mark error on first line beyond limit
Location location = new Location(lineNumber, lengthLimit + 1);
this.printer.error(rule, Messages.LINE + msg + lengthVersusLimit, location);
}
private void trailingWhitespaceViolation(int lineNumber, int lineLength) {
Location location = new Location(lineNumber, lineLength);
this.printer.warn(Rules.TRAILING_WHITESPACE, Messages.LINE + Messages.TRAILING_WHITESPACE, location);
}
private void verifyFileLength() {
if (SourceFileUtil.fileTooLong(this.numOfLines, this.constructLengths.maxFileLength)) {
String lengthVersusLimit = " (" + this.numOfLines + "/" + this.constructLengths.maxFileLength + ")";
// Mark error on first line beyond limit
Location location = new Location(this.constructLengths.maxFileLength + 1);
this.printer.error(Rules.MAX_FILE_LENGTH, Messages.FILE + Messages.EXCEEDS_LINE_LIMIT + lengthVersusLimit,
location);
}
}
private void verifyNewlineTerminated() throws IOException {
if (!SourceFileUtil.singleNewlineTerminated(this.inputFile)) {
Location location = new Location(this.numOfLines);
this.printer.error(Rules.TERMINATING_NEWLINE, Messages.FILE + Messages.NEWLINE_TERMINATOR, location);
}
}
private void verifyNoLeadingWhitespace() throws IOException {
if (SourceFileUtil.hasLeadingWhitespace(this.inputFile)) {
Location location = new Location(1, 1);
this.printer.warn(Rules.LEADING_WHITESPACE, Messages.FILE + Messages.LEADING_WHITESPACE, location);
}
}
protected int getNumOfLines() {
return numOfLines;
}
}