View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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.CommonUtils;
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   * @author maxvetrenko
83   */
84  @StatelessCheck
85  public class SeparatorWrapCheck
86      extends AbstractCheck {
87  
88      /**
89       * A key is pointing to the warning message text in "messages.properties"
90       * file.
91       */
92      public static final String MSG_LINE_PREVIOUS = "line.previous";
93  
94      /**
95       * A key is pointing to the warning message text in "messages.properties"
96       * file.
97       */
98      public static final String MSG_LINE_NEW = "line.new";
99  
100     /** The policy to enforce. */
101     private WrapOption option = WrapOption.EOL;
102 
103     /**
104      * Set the option to enforce.
105      * @param optionStr string to decode option from
106      * @throws IllegalArgumentException if unable to decode
107      */
108     public void setOption(String optionStr) {
109         try {
110             option = WrapOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
111         }
112         catch (IllegalArgumentException iae) {
113             throw new IllegalArgumentException("unable to parse " + optionStr, iae);
114         }
115     }
116 
117     @Override
118     public int[] getDefaultTokens() {
119         return new int[] {
120             TokenTypes.DOT,
121             TokenTypes.COMMA,
122         };
123     }
124 
125     @Override
126     public int[] getAcceptableTokens() {
127         return new int[] {
128             TokenTypes.DOT,
129             TokenTypes.COMMA,
130             TokenTypes.SEMI,
131             TokenTypes.ELLIPSIS,
132             TokenTypes.AT,
133             TokenTypes.LPAREN,
134             TokenTypes.RPAREN,
135             TokenTypes.ARRAY_DECLARATOR,
136             TokenTypes.RBRACK,
137             TokenTypes.METHOD_REF,
138         };
139     }
140 
141     @Override
142     public int[] getRequiredTokens() {
143         return CommonUtils.EMPTY_INT_ARRAY;
144     }
145 
146     @Override
147     public void visitToken(DetailAST ast) {
148         final String text = ast.getText();
149         final int colNo = ast.getColumnNo();
150         final int lineNo = ast.getLineNo();
151         final String currentLine = getLines()[lineNo - 1];
152         final String substringAfterToken =
153                 currentLine.substring(colNo + text.length()).trim();
154         final String substringBeforeToken =
155                 currentLine.substring(0, colNo).trim();
156 
157         if (option == WrapOption.EOL
158                 && substringBeforeToken.isEmpty()) {
159             log(lineNo, colNo, MSG_LINE_PREVIOUS, text);
160         }
161         else if (option == WrapOption.NL
162                  && substringAfterToken.isEmpty()) {
163             log(lineNo, colNo, MSG_LINE_NEW, text);
164         }
165     }
166 }