All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.puppycrawl.tools.checkstyle.checks.whitespace.GenericWhitespaceCheck Maven / Gradle / Ivy

Go to download

Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard

There is a newer version: 10.18.1
Show newest version
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2021 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
////////////////////////////////////////////////////////////////////////////////

package com.puppycrawl.tools.checkstyle.checks.whitespace;

import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.utils.CommonUtil;

/**
 * 

* Checks that the whitespace around the Generic tokens (angle brackets) * "<" and ">" are correct to the typical convention. * The convention is not configurable. *

*

* Left angle bracket ("<"): *

*
    *
  • should be preceded with whitespace only * in generic methods definitions.
  • *
  • should not be preceded with whitespace * when it is precede method name or constructor.
  • *
  • should not be preceded with whitespace when following type name.
  • *
  • should not be followed with whitespace in all cases.
  • *
*

* Right angle bracket (">"): *

*
    *
  • should not be preceded with whitespace in all cases.
  • *
  • should be followed with whitespace in almost all cases, * except diamond operators and when preceding method name or constructor.
*

* To configure the check: *

*
 * <module name="GenericWhitespace"/>
 * 
*

* Examples with correct spacing: *

*
 * // Generic methods definitions
 * public void <K, V extends Number> boolean foo(K, V) {}
 * // Generic type definition
 * class name<T1, T2, ..., Tn> {}
 * // Generic type reference
 * OrderedPair<String, Box<Integer>> p;
 * // Generic preceded method name
 * boolean same = Util.<Integer, String>compare(p1, p2);
 * // Diamond operator
 * Pair<Integer, String> p1 = new Pair<>(1, "apple");
 * // Method reference
 * List<T> list = ImmutableList.Builder<T>::new;
 * // Method reference
 * sort(list, Comparable::<String>compareTo);
 * // Constructor call
 * MyClass obj = new <String>MyClass();
 * 
*

* Examples with incorrect spacing: *

*
 * List< String> l; // violation, "<" followed by whitespace
 * Box b = Box. <String>of("foo"); // violation, "<" preceded with whitespace
 * public<T> void foo() {} // violation, "<" not preceded with whitespace
 *
 * List a = new ArrayList<> (); // violation, ">" followed by whitespace
 * Map<Integer, String>m; // violation, ">" not followed by whitespace
 * Pair<Integer, Integer > p; // violation, ">" preceded with whitespace
 * 
*

* Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} *

*

* Violation Message Keys: *

*
    *
  • * {@code ws.followed} *
  • *
  • * {@code ws.illegalFollow} *
  • *
  • * {@code ws.notPreceded} *
  • *
  • * {@code ws.preceded} *
  • *
* * @since 5.0 */ @FileStatefulCheck public class GenericWhitespaceCheck extends AbstractCheck { /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_WS_PRECEDED = "ws.preceded"; /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_WS_FOLLOWED = "ws.followed"; /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_WS_NOT_PRECEDED = "ws.notPreceded"; /** * A key is pointing to the warning message text in "messages.properties" * file. */ public static final String MSG_WS_ILLEGAL_FOLLOW = "ws.illegalFollow"; /** Open angle bracket literal. */ private static final String OPEN_ANGLE_BRACKET = "<"; /** Close angle bracket literal. */ private static final String CLOSE_ANGLE_BRACKET = ">"; /** Used to count the depth of a Generic expression. */ private int depth; @Override public int[] getDefaultTokens() { return getRequiredTokens(); } @Override public int[] getAcceptableTokens() { return getRequiredTokens(); } @Override public int[] getRequiredTokens() { return new int[] {TokenTypes.GENERIC_START, TokenTypes.GENERIC_END}; } @Override public void beginTree(DetailAST rootAST) { // Reset for each tree, just increase there are violations in preceding // trees. depth = 0; } @Override public void visitToken(DetailAST ast) { switch (ast.getType()) { case TokenTypes.GENERIC_START: processStart(ast); depth++; break; case TokenTypes.GENERIC_END: processEnd(ast); depth--; break; default: throw new IllegalArgumentException("Unknown type " + ast); } } /** * Checks the token for the end of Generics. * * @param ast the token to check */ private void processEnd(DetailAST ast) { final String line = getLine(ast.getLineNo() - 1); final int before = ast.getColumnNo() - 1; final int after = ast.getColumnNo() + 1; if (before >= 0 && Character.isWhitespace(line.charAt(before)) && !containsWhitespaceBefore(before, line)) { log(ast, MSG_WS_PRECEDED, CLOSE_ANGLE_BRACKET); } if (after < line.length()) { // Check if the last Generic, in which case must be a whitespace // or a '(),[.'. if (depth == 1) { processSingleGeneric(ast, line, after); } else { processNestedGenerics(ast, line, after); } } } /** * Process Nested generics. * * @param ast token * @param line line content * @param after position after */ private void processNestedGenerics(DetailAST ast, String line, int after) { // In a nested Generic type, so can only be a '>' or ',' or '&' // In case of several extends definitions: // // class IntEnumValueType & IntEnum> // ^ // should be whitespace if followed by & -+ // final int indexOfAmp = line.indexOf('&', after); if (indexOfAmp >= 1 && containsWhitespaceBetween(after, indexOfAmp, line)) { if (indexOfAmp - after == 0) { log(ast, MSG_WS_NOT_PRECEDED, "&"); } else if (indexOfAmp - after != 1) { log(ast, MSG_WS_FOLLOWED, CLOSE_ANGLE_BRACKET); } } else if (line.charAt(after) == ' ') { log(ast, MSG_WS_FOLLOWED, CLOSE_ANGLE_BRACKET); } } /** * Process Single-generic. * * @param ast token * @param line line content * @param after position after */ private void processSingleGeneric(DetailAST ast, String line, int after) { final char charAfter = line.charAt(after); if (isGenericBeforeMethod(ast) || isGenericBeforeCtor(ast)) { if (Character.isWhitespace(charAfter)) { log(ast, MSG_WS_FOLLOWED, CLOSE_ANGLE_BRACKET); } } else if (!isCharacterValidAfterGenericEnd(charAfter)) { log(ast, MSG_WS_ILLEGAL_FOLLOW, CLOSE_ANGLE_BRACKET); } } /** * Checks if generic is before constructor invocation. * * @param ast ast * @return true if generic before a constructor invocation */ private static boolean isGenericBeforeCtor(DetailAST ast) { final DetailAST parent = ast.getParent(); return parent.getParent().getType() == TokenTypes.LITERAL_NEW && (parent.getNextSibling().getType() == TokenTypes.IDENT || parent.getNextSibling().getType() == TokenTypes.DOT); } /** * Is generic before method reference. * * @param ast ast * @return true if generic before a method ref */ private static boolean isGenericBeforeMethod(DetailAST ast) { return ast.getParent().getParent().getParent().getType() == TokenTypes.METHOD_CALL || isAfterMethodReference(ast); } /** * Checks if current generic end ('>') is located after * {@link TokenTypes#METHOD_REF method reference operator}. * * @param genericEnd {@link TokenTypes#GENERIC_END} * @return true if '>' follows after method reference. */ private static boolean isAfterMethodReference(DetailAST genericEnd) { return genericEnd.getParent().getParent().getType() == TokenTypes.METHOD_REF; } /** * Checks the token for the start of Generics. * * @param ast the token to check */ private void processStart(DetailAST ast) { final String line = getLine(ast.getLineNo() - 1); final int before = ast.getColumnNo() - 1; final int after = ast.getColumnNo() + 1; // Need to handle two cases as in: // // public static Callable callable(Runnable task, T result) // ^ ^ // ws reqd ---+ +--- whitespace NOT required // if (before >= 0) { // Detect if the first case final DetailAST parent = ast.getParent(); final DetailAST grandparent = parent.getParent(); if (grandparent.getType() == TokenTypes.CTOR_DEF || grandparent.getType() == TokenTypes.METHOD_DEF || isGenericBeforeCtor(ast)) { // Require whitespace if (!Character.isWhitespace(line.charAt(before))) { log(ast, MSG_WS_NOT_PRECEDED, OPEN_ANGLE_BRACKET); } } // Whitespace not required else if (Character.isWhitespace(line.charAt(before)) && !containsWhitespaceBefore(before, line)) { log(ast, MSG_WS_PRECEDED, OPEN_ANGLE_BRACKET); } } if (after < line.length() && Character.isWhitespace(line.charAt(after))) { log(ast, MSG_WS_FOLLOWED, OPEN_ANGLE_BRACKET); } } /** * Returns whether the specified string contains only whitespace between * specified indices. * * @param fromIndex the index to start the search from. Inclusive * @param toIndex the index to finish the search. Exclusive * @param line the line to check * @return whether there are only whitespaces (or nothing) */ private static boolean containsWhitespaceBetween(int fromIndex, int toIndex, String line) { boolean result = true; for (int i = fromIndex; i < toIndex; i++) { if (!Character.isWhitespace(line.charAt(i))) { result = false; break; } } return result; } /** * Returns whether the specified string contains only whitespace up to specified index. * * @param before the index to start the search from. Inclusive * @param line the index to finish the search. Exclusive * @return {@code true} if there are only whitespaces, * false if there is nothing before or some other characters */ private static boolean containsWhitespaceBefore(int before, String line) { return before != 0 && CommonUtil.hasWhitespaceBefore(before, line); } /** * Checks whether given character is valid to be right after generic ends. * * @param charAfter character to check * @return checks if given character is valid */ private static boolean isCharacterValidAfterGenericEnd(char charAfter) { return charAfter == '(' || charAfter == ')' || charAfter == ',' || charAfter == '[' || charAfter == '.' || charAfter == ':' || charAfter == ';' || Character.isWhitespace(charAfter); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy