package com.sleekbyte.tailor.utils; 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 org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; /** * Contains utility functions that are used by various whitespace listeners to verify whitespace. */ public final class WhitespaceVerifier { private Printer printer; private Rules rule; public WhitespaceVerifier(Printer printer, Rules rule) { this.printer = printer; this.rule = rule; } public boolean checkLeftSpaces(Token left, Token op, int numSpaces) { return op.getLine() == left.getLine() && op.getCharPositionInLine() - ListenerUtil.getLastCharPositionInLine(left) != numSpaces + 1; } public boolean checkRightSpaces(Token right, Token op, int numSpaces) { return right.getLine() == op.getLine() && right.getCharPositionInLine() - ListenerUtil.getLastCharPositionInLine(op) != numSpaces + 1; } public boolean checkIfInline(Token one, Token two) { return one.getLine() != two.getLine(); } /** * Verifies if a certain punctuation token is left associated (no space on the left). * * @param left Token on the left of the punctuation token * @param punc Punctuation token * @param puncStr String version of the punctuation to be used in violation messages */ public void verifyPunctuationLeftAssociation(Token left, Token punc, String puncStr) { Location puncLocation = ListenerUtil.getTokenLocation(punc); if (checkIfInline(left, punc) || checkLeftSpaces(left, punc, 0)) { printer.error(rule, puncStr + Messages.AT_COLUMN + puncLocation.column + " " + Messages.NO_SPACE_BEFORE, puncLocation); } } /** * Verifies if a certain punctuation token is left associated (no space on the left, one space on the right). * * @param left Token on the left of the punctuation token * @param right Token on the right of the punctuation token * @param punc Punctuation token * @param puncStr String version of the punctuation to be used in violation messages */ public void verifyPunctuationLeftAssociation(Token left, Token right, Token punc, String puncStr) { Location puncLocation = ListenerUtil.getTokenLocation(punc); verifyPunctuationLeftAssociation(left, punc, puncStr); if (checkRightSpaces(right, punc, 1)) { printer.error(rule, puncStr + Messages.AT_COLUMN + puncLocation.column + " " + Messages.SPACE_AFTER, puncLocation); } } /** * Verifies if a particular punctuation token is space delimited (single space on either side). * * @param left Token on the left of the punctuation token * @param right Token on the right of the punctuation token * @param punc Punctuation token * @param puncStr String version of the punctuation to be used in violation messages */ public void verifyPunctuationIsSpaceDelimited(Token left, Token right, Token punc, String puncStr) { Location puncLocation = ListenerUtil.getTokenLocation(punc); if (checkLeftSpaces(left, punc, 1)) { printer.error(rule, puncStr + Messages.AT_COLUMN + puncLocation.column + " " + Messages.SPACE_BEFORE, puncLocation); } if (checkRightSpaces(right, punc, 1)) { printer.error(rule, puncStr + Messages.AT_COLUMN + puncLocation.column + " " + Messages.SPACE_AFTER, puncLocation); } } /** * Verifies if a bracket construct does not contain whitespace immediately after the opening bracket and * immediately before the closing bracket. * * @param ctx Context comprised of brackets * @param construct Name of construct */ public void verifyBracketContentWhitespace(ParserRuleContext ctx, String construct) { Token openingParenthesis = ParseTreeUtil.getStopTokenForNode(ctx.getChild(0)); Token closingParenthesis = ParseTreeUtil.getStopTokenForNode(ctx.getChild(ctx.getChildCount() - 1)); Location openingParenthesisLoc = ListenerUtil.getTokenLocation(openingParenthesis); // Handles cases where the parentheses only contain whitespace // Example: if ( ) {} if (ctx.getChildCount() == 2) { if (checkLeftSpaces(openingParenthesis, closingParenthesis, 0)) { printer.error(rule, Messages.EMPTY + construct.toLowerCase() + Messages.ILLEGAL_WHITESPACE, new Location(openingParenthesisLoc.line, openingParenthesisLoc.column + 1) ); } return; } Token contentStart = ParseTreeUtil.getStartTokenForNode(ctx.getChild(1)); Token contentEnd = ParseTreeUtil.getStopTokenForNode(ctx.getChild(ctx.getChildCount() - 2)); if (checkLeftSpaces(openingParenthesis, contentStart, 0)) { printer.error(rule, construct + Messages.CONTENT + Messages.LEADING_WHITESPACE, new Location(openingParenthesisLoc.line, openingParenthesisLoc.column + 1)); } if (checkRightSpaces(closingParenthesis, contentEnd, 0)) { Location contentStopLocation = ListenerUtil.getTokenEndLocation(contentEnd); printer.error(rule, construct + Messages.CONTENT + Messages.NOT_END_SPACE, contentStopLocation); } } /** * Verifies that bracket constructs do not have a whitespace before the opening bracket. * * @param ctx Context comprised of brackets * @param construct Name of construct * @param message Violation message * @param numSpace Number of spaces to check for */ public void verifyLeadingWhitespaceBeforeBracket(ParserRuleContext ctx, String construct, String message, int numSpace) { Token left = ParseTreeUtil.getStopTokenForNode(ParseTreeUtil.getLeftNode(ctx)); Token openingParenthesis = ParseTreeUtil.getStartTokenForNode(ctx.getChild(0)); if (checkLeftSpaces(left, openingParenthesis, numSpace)) { Location illegalWhitespaceLocation = ListenerUtil.getTokenEndLocation(left); printer.error(rule, construct + message, illegalWhitespaceLocation); } } /** * Verifies that bracket constructs do not have a whitespace before the opening bracket. * * @param ctx Context comprised of brackets * @param message Violation message * @param numSpace Number of spaces to check for */ public void verifyLeadingWhitespaceBeforeBracket(ParserRuleContext ctx, String message, int numSpace) { Token left = ParseTreeUtil.getStopTokenForNode(ParseTreeUtil.getLeftNode(ctx)); Token openingParenthesis = ParseTreeUtil.getStartTokenForNode(ctx.getChild(0)); if (checkLeftSpaces(left, openingParenthesis, numSpace)) { Location illegalWhitespaceLocation = ListenerUtil.getTokenEndLocation(left); printer.error(rule, message, illegalWhitespaceLocation); } } }