1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.Locale;
23  
24  import com.puppycrawl.tools.checkstyle.StatelessCheck;
25  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
26  import com.puppycrawl.tools.checkstyle.api.DetailAST;
27  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
28  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
29  
30  /**
31   * <p>
32   * Checks line wrapping with separators.
33   * The policy to verify is specified using the {@link WrapOption} class
34   * and defaults to {@link WrapOption#EOL}.
35   * </p>
36   * <p> By default the check will check the following separators:
37   *  {@link TokenTypes#DOT DOT},
38   *  {@link TokenTypes#COMMA COMMA},
39   * Other acceptable tokens are
40   *  {@link TokenTypes#SEMI SEMI},
41   *  {@link TokenTypes#ELLIPSIS ELLIPSIS},
42   *  {@link TokenTypes#AT AT},
43   *  {@link TokenTypes#LPAREN LPAREN},
44   *  {@link TokenTypes#RPAREN RPAREN},
45   *  {@link TokenTypes#ARRAY_DECLARATOR ARRAY_DECLARATOR},
46   *  {@link TokenTypes#RBRACK RBRACK},
47   * </p>
48   * <p>
49   * Code example for comma and dot at the new line:
50   * </p>
51   * <pre>
52   * s
53   *    .isEmpty();
54   * foo(i
55   *    ,s);
56   * </pre>
57   *  <p>
58   * An example of how to configure the check is:
59   * </p>
60   * <pre>
61   * &lt;module name="SeparatorWrap"/&gt;
62   * </pre>
63   * <p>
64   * Code example for comma and dot at the previous line:
65   * </p>
66   * <pre>
67   * s.
68   *    isEmpty();
69   * foo(i,
70   *    s);
71   * </pre>
72   * <p> An example of how to configure the check for comma at the
73   * new line is:
74   * </p>
75   * <pre>
76   * &lt;module name="SeparatorWrap"&gt;
77   *     &lt;property name="tokens" value="COMMA"/&gt;
78   *     &lt;property name="option" value="nl"/&gt;
79   * &lt;/module&gt;
80   * </pre>
81   *
82   */
83  @StatelessCheck
84  public class SeparatorWrapCheck
85      extends AbstractCheck {
86  
87      /**
88       * A key is pointing to the warning message text in "messages.properties"
89       * file.
90       */
91      public static final String MSG_LINE_PREVIOUS = "line.previous";
92  
93      /**
94       * A key is pointing to the warning message text in "messages.properties"
95       * file.
96       */
97      public static final String MSG_LINE_NEW = "line.new";
98  
99      /** The policy to enforce. */
100     private WrapOption option = WrapOption.EOL;
101 
102     /**
103      * Set the option to enforce.
104      * @param optionStr string to decode option from
105      * @throws IllegalArgumentException if unable to decode
106      */
107     public void setOption(String optionStr) {
108         option = WrapOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
109     }
110 
111     @Override
112     public int[] getDefaultTokens() {
113         return new int[] {
114             TokenTypes.DOT,
115             TokenTypes.COMMA,
116         };
117     }
118 
119     @Override
120     public int[] getAcceptableTokens() {
121         return new int[] {
122             TokenTypes.DOT,
123             TokenTypes.COMMA,
124             TokenTypes.SEMI,
125             TokenTypes.ELLIPSIS,
126             TokenTypes.AT,
127             TokenTypes.LPAREN,
128             TokenTypes.RPAREN,
129             TokenTypes.ARRAY_DECLARATOR,
130             TokenTypes.RBRACK,
131             TokenTypes.METHOD_REF,
132         };
133     }
134 
135     @Override
136     public int[] getRequiredTokens() {
137         return CommonUtil.EMPTY_INT_ARRAY;
138     }
139 
140     @Override
141     public void visitToken(DetailAST ast) {
142         final String text = ast.getText();
143         final int colNo = ast.getColumnNo();
144         final int lineNo = ast.getLineNo();
145         final String currentLine = getLines()[lineNo - 1];
146         final String substringAfterToken =
147                 currentLine.substring(colNo + text.length()).trim();
148         final String substringBeforeToken =
149                 currentLine.substring(0, colNo).trim();
150 
151         if (option == WrapOption.EOL
152                 && substringBeforeToken.isEmpty()) {
153             log(ast, MSG_LINE_PREVIOUS, text);
154         }
155         else if (option == WrapOption.NL
156                  && substringAfterToken.isEmpty()) {
157             log(ast, MSG_LINE_NEW, text);
158         }
159     }
160 
161 }