View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2018 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 for operators.
33   * The policy to verify is specified using the {@link WrapOption} class
34   * and defaults to {@link WrapOption#NL}.
35   * </p>
36   * <p> By default the check will check the following operators:
37   *  {@link TokenTypes#BAND BAND},
38   *  {@link TokenTypes#BOR BOR},
39   *  {@link TokenTypes#BSR BSR},
40   *  {@link TokenTypes#BXOR BXOR},
41   *  {@link TokenTypes#COLON COLON},
42   *  {@link TokenTypes#DIV DIV},
43   *  {@link TokenTypes#EQUAL EQUAL},
44   *  {@link TokenTypes#GE GE},
45   *  {@link TokenTypes#GT GT},
46   *  {@link TokenTypes#LAND LAND},
47   *  {@link TokenTypes#LE LE},
48   *  {@link TokenTypes#LITERAL_INSTANCEOF LITERAL_INSTANCEOF},
49   *  {@link TokenTypes#LOR LOR},
50   *  {@link TokenTypes#LT LT},
51   *  {@link TokenTypes#MINUS MINUS},
52   *  {@link TokenTypes#MOD MOD},
53   *  {@link TokenTypes#NOT_EQUAL NOT_EQUAL},
54   *  {@link TokenTypes#PLUS PLUS},
55   *  {@link TokenTypes#QUESTION QUESTION},
56   *  {@link TokenTypes#SL SL},
57   *  {@link TokenTypes#SR SR},
58   *  {@link TokenTypes#STAR STAR}.
59   * Other acceptable tokens are
60   *  {@link TokenTypes#ASSIGN ASSIGN},
61   *  {@link TokenTypes#BAND_ASSIGN BAND_ASSIGN},
62   *  {@link TokenTypes#BOR_ASSIGN BOR_ASSIGN},
63   *  {@link TokenTypes#BSR_ASSIGN BSR_ASSIGN},
64   *  {@link TokenTypes#BXOR_ASSIGN BXOR_ASSIGN},
65   *  {@link TokenTypes#DIV_ASSIGN DIV_ASSIGN},
66   *  {@link TokenTypes#MINUS_ASSIGN MINUS_ASSIGN},
67   *  {@link TokenTypes#MOD_ASSIGN MOD_ASSIGN},
68   *  {@link TokenTypes#PLUS_ASSIGN PLUS_ASSIGN},
69   *  {@link TokenTypes#SL_ASSIGN SL_ASSIGN},
70   *  {@link TokenTypes#SR_ASSIGN SR_ASSIGN},
71   *  {@link TokenTypes#STAR_ASSIGN STAR_ASSIGN}.
72   *  {@link TokenTypes#METHOD_REF METHOD_REF}.
73   * </p>
74   *  <p>
75   * An example of how to configure the check is:
76   * </p>
77   * <pre>
78   * &lt;module name="OperatorWrap"/&gt;
79   * </pre>
80   * <p> An example of how to configure the check for assignment operators at the
81   * end of a line is:
82   * </p>
83   * <pre>
84   * &lt;module name="OperatorWrap"&gt;
85   *     &lt;property name="tokens"
86   *               value="ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,MOD_ASSIGN
87   *               ,SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,BAND_ASSIGN"/&gt;
88   *     &lt;property name="option" value="eol"/&gt;
89   * &lt;/module&gt;
90   * </pre>
91   *
92   * @author Rick Giles
93   */
94  @StatelessCheck
95  public class OperatorWrapCheck
96      extends AbstractCheck {
97  
98      /**
99       * A key is pointing to the warning message text in "messages.properties"
100      * file.
101      */
102     public static final String MSG_LINE_NEW = "line.new";
103 
104     /**
105      * A key is pointing to the warning message text in "messages.properties"
106      * file.
107      */
108     public static final String MSG_LINE_PREVIOUS = "line.previous";
109 
110     /** The policy to enforce. */
111     private WrapOption option = WrapOption.NL;
112 
113     /**
114      * Set the option to enforce.
115      * @param optionStr string to decode option from
116      * @throws IllegalArgumentException if unable to decode
117      */
118     public void setOption(String optionStr) {
119         try {
120             option = WrapOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
121         }
122         catch (IllegalArgumentException iae) {
123             throw new IllegalArgumentException("unable to parse " + optionStr, iae);
124         }
125     }
126 
127     @Override
128     public int[] getDefaultTokens() {
129         return new int[] {
130             TokenTypes.QUESTION,          // '?'
131             TokenTypes.COLON,             // ':' (not reported for a case)
132             TokenTypes.EQUAL,             // "=="
133             TokenTypes.NOT_EQUAL,         // "!="
134             TokenTypes.DIV,               // '/'
135             TokenTypes.PLUS,              //' +' (unary plus is UNARY_PLUS)
136             TokenTypes.MINUS,             // '-' (unary minus is UNARY_MINUS)
137             TokenTypes.STAR,              // '*'
138             TokenTypes.MOD,               // '%'
139             TokenTypes.SR,                // ">>"
140             TokenTypes.BSR,               // ">>>"
141             TokenTypes.GE,                // ">="
142             TokenTypes.GT,                // ">"
143             TokenTypes.SL,                // "<<"
144             TokenTypes.LE,                // "<="
145             TokenTypes.LT,                // '<'
146             TokenTypes.BXOR,              // '^'
147             TokenTypes.BOR,               // '|'
148             TokenTypes.LOR,               // "||"
149             TokenTypes.BAND,              // '&'
150             TokenTypes.LAND,              // "&&"
151             TokenTypes.TYPE_EXTENSION_AND,
152             TokenTypes.LITERAL_INSTANCEOF,
153         };
154     }
155 
156     @Override
157     public int[] getAcceptableTokens() {
158         return new int[] {
159             TokenTypes.QUESTION,          // '?'
160             TokenTypes.COLON,             // ':' (not reported for a case)
161             TokenTypes.EQUAL,             // "=="
162             TokenTypes.NOT_EQUAL,         // "!="
163             TokenTypes.DIV,               // '/'
164             TokenTypes.PLUS,              //' +' (unary plus is UNARY_PLUS)
165             TokenTypes.MINUS,             // '-' (unary minus is UNARY_MINUS)
166             TokenTypes.STAR,              // '*'
167             TokenTypes.MOD,               // '%'
168             TokenTypes.SR,                // ">>"
169             TokenTypes.BSR,               // ">>>"
170             TokenTypes.GE,                // ">="
171             TokenTypes.GT,                // ">"
172             TokenTypes.SL,                // "<<"
173             TokenTypes.LE,                // "<="
174             TokenTypes.LT,                // '<'
175             TokenTypes.BXOR,              // '^'
176             TokenTypes.BOR,               // '|'
177             TokenTypes.LOR,               // "||"
178             TokenTypes.BAND,              // '&'
179             TokenTypes.LAND,              // "&&"
180             TokenTypes.LITERAL_INSTANCEOF,
181             TokenTypes.TYPE_EXTENSION_AND,
182             TokenTypes.ASSIGN,            // '='
183             TokenTypes.DIV_ASSIGN,        // "/="
184             TokenTypes.PLUS_ASSIGN,       // "+="
185             TokenTypes.MINUS_ASSIGN,      //"-="
186             TokenTypes.STAR_ASSIGN,       // "*="
187             TokenTypes.MOD_ASSIGN,        // "%="
188             TokenTypes.SR_ASSIGN,         // ">>="
189             TokenTypes.BSR_ASSIGN,        // ">>>="
190             TokenTypes.SL_ASSIGN,         // "<<="
191             TokenTypes.BXOR_ASSIGN,       // "^="
192             TokenTypes.BOR_ASSIGN,        // "|="
193             TokenTypes.BAND_ASSIGN,       // "&="
194             TokenTypes.METHOD_REF,        // "::"
195         };
196     }
197 
198     @Override
199     public int[] getRequiredTokens() {
200         return CommonUtils.EMPTY_INT_ARRAY;
201     }
202 
203     @Override
204     public void visitToken(DetailAST ast) {
205         final DetailAST parent = ast.getParent();
206         //we do not want to check colon for cases and defaults
207         if (ast.getType() != TokenTypes.COLON
208                 || parent.getType() != TokenTypes.LITERAL_DEFAULT
209                     && parent.getType() != TokenTypes.LITERAL_CASE) {
210             final String text = ast.getText();
211             final int colNo = ast.getColumnNo();
212             final int lineNo = ast.getLineNo();
213             final String currentLine = getLine(lineNo - 1);
214 
215             // Check if rest of line is whitespace, and not just the operator
216             // by itself. This last bit is to handle the operator on a line by
217             // itself.
218             if (option == WrapOption.NL
219                     && !text.equals(currentLine.trim())
220                     && CommonUtils.isBlank(currentLine.substring(colNo + text.length()))) {
221                 log(lineNo, colNo, MSG_LINE_NEW, text);
222             }
223             else if (option == WrapOption.EOL
224                     && CommonUtils.hasWhitespaceBefore(colNo - 1, currentLine)) {
225                 log(lineNo, colNo, MSG_LINE_PREVIOUS, text);
226             }
227         }
228     }
229 
230 }