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