View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.whitespace;
21  
22  import java.util.Arrays;
23  import java.util.Locale;
24  
25  import com.puppycrawl.tools.checkstyle.StatelessCheck;
26  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
27  import com.puppycrawl.tools.checkstyle.api.DetailAST;
28  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
29  import com.puppycrawl.tools.checkstyle.utils.CodePointUtil;
30  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
31  
32  /**
33   * <p>
34   * Checks line wrapping with separators.
35   * </p>
36   * <ul>
37   * <li>
38   * Property {@code option} - Specify policy on how to wrap lines.
39   * Type is {@code com.puppycrawl.tools.checkstyle.checks.whitespace.WrapOption}.
40   * Default value is {@code eol}.
41   * </li>
42   * <li>
43   * Property {@code tokens} - tokens to check
44   * Type is {@code java.lang.String[]}.
45   * Validation type is {@code tokenSet}.
46   * Default value is:
47   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#DOT">
48   * DOT</a>,
49   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMMA">
50   * COMMA</a>.
51   * </li>
52   * </ul>
53   * <p>
54   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
55   * </p>
56   * <p>
57   * Violation Message Keys:
58   * </p>
59   * <ul>
60   * <li>
61   * {@code line.new}
62   * </li>
63   * <li>
64   * {@code line.previous}
65   * </li>
66   * </ul>
67   *
68   * @since 5.8
69   */
70  @StatelessCheck
71  public class SeparatorWrapCheck
72      extends AbstractCheck {
73  
74      /**
75       * A key is pointing to the warning message text in "messages.properties"
76       * file.
77       */
78      public static final String MSG_LINE_PREVIOUS = "line.previous";
79  
80      /**
81       * A key is pointing to the warning message text in "messages.properties"
82       * file.
83       */
84      public static final String MSG_LINE_NEW = "line.new";
85  
86      /** Specify policy on how to wrap lines. */
87      private WrapOption option = WrapOption.EOL;
88  
89      /**
90       * Setter to specify policy on how to wrap lines.
91       *
92       * @param optionStr string to decode option from
93       * @throws IllegalArgumentException if unable to decode
94       * @since 5.8
95       */
96      public void setOption(String optionStr) {
97          option = WrapOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
98      }
99  
100     @Override
101     public int[] getDefaultTokens() {
102         return new int[] {
103             TokenTypes.DOT,
104             TokenTypes.COMMA,
105         };
106     }
107 
108     @Override
109     public int[] getAcceptableTokens() {
110         return new int[] {
111             TokenTypes.DOT,
112             TokenTypes.COMMA,
113             TokenTypes.SEMI,
114             TokenTypes.ELLIPSIS,
115             TokenTypes.AT,
116             TokenTypes.LPAREN,
117             TokenTypes.RPAREN,
118             TokenTypes.ARRAY_DECLARATOR,
119             TokenTypes.RBRACK,
120             TokenTypes.METHOD_REF,
121         };
122     }
123 
124     @Override
125     public int[] getRequiredTokens() {
126         return CommonUtil.EMPTY_INT_ARRAY;
127     }
128 
129     @Override
130     public void visitToken(DetailAST ast) {
131         final String text = ast.getText();
132         final int colNo = ast.getColumnNo();
133         final int lineNo = ast.getLineNo();
134         final int[] currentLine = getLineCodePoints(lineNo - 1);
135         final boolean isLineEmptyAfterToken = CodePointUtil.isBlank(
136                 Arrays.copyOfRange(currentLine, colNo + text.length(), currentLine.length)
137         );
138         final boolean isLineEmptyBeforeToken = CodePointUtil.isBlank(
139                 Arrays.copyOfRange(currentLine, 0, colNo)
140         );
141 
142         if (option == WrapOption.NL
143                  && isLineEmptyAfterToken) {
144             log(ast, MSG_LINE_NEW, text);
145         }
146         else if (option == WrapOption.EOL
147                 && isLineEmptyBeforeToken) {
148             log(ast, MSG_LINE_PREVIOUS, text);
149         }
150     }
151 
152 }