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 * @version 1.0
89 */
90 public class OperatorWrapCheck
91 extends AbstractOptionCheck<WrapOption>
92 {
93
94 /**
95 * A key is pointing to the warning message text in "messages.properties"
96 * file.
97 */
98 public static final String LINE_NEW = "line.new";
99
100 /**
101 * A key is pointing to the warning message text in "messages.properties"
102 * file.
103 */
104 public static final String LINE_PREVIOUS = "line.previous";
105
106 /**
107 * Sets the operator wrap option to new line.
108 */
109 public OperatorWrapCheck()
110 {
111 super(WrapOption.NL, WrapOption.class);
112 }
113
114 @Override
115 public int[] getDefaultTokens()
116 {
117 return new int[] {
118 TokenTypes.QUESTION, // '?'
119 TokenTypes.COLON, // ':' (not reported for a case)
120 TokenTypes.EQUAL, // "=="
121 TokenTypes.NOT_EQUAL, // "!="
122 TokenTypes.DIV, // '/'
123 TokenTypes.PLUS, //' +' (unary plus is UNARY_PLUS)
124 TokenTypes.MINUS, // '-' (unary minus is UNARY_MINUS)
125 TokenTypes.STAR, // '*'
126 TokenTypes.MOD, // '%'
127 TokenTypes.SR, // ">>"
128 TokenTypes.BSR, // ">>>"
129 TokenTypes.GE, // ">="
130 TokenTypes.GT, // ">"
131 TokenTypes.SL, // "<<"
132 TokenTypes.LE, // "<="
133 TokenTypes.LT, // '<'
134 TokenTypes.BXOR, // '^'
135 TokenTypes.BOR, // '|'
136 TokenTypes.LOR, // "||"
137 TokenTypes.BAND, // '&'
138 TokenTypes.LAND, // "&&"
139 TokenTypes.TYPE_EXTENSION_AND,
140 TokenTypes.LITERAL_INSTANCEOF,
141 };
142 }
143
144 @Override
145 public int[] getAcceptableTokens()
146 {
147 return new int[] {
148 TokenTypes.QUESTION, // '?'
149 TokenTypes.COLON, // ':' (not reported for a case)
150 TokenTypes.EQUAL, // "=="
151 TokenTypes.NOT_EQUAL, // "!="
152 TokenTypes.DIV, // '/'
153 TokenTypes.PLUS, //' +' (unary plus is UNARY_PLUS)
154 TokenTypes.MINUS, // '-' (unary minus is UNARY_MINUS)
155 TokenTypes.STAR, // '*'
156 TokenTypes.MOD, // '%'
157 TokenTypes.SR, // ">>"
158 TokenTypes.BSR, // ">>>"
159 TokenTypes.GE, // ">="
160 TokenTypes.GT, // ">"
161 TokenTypes.SL, // "<<"
162 TokenTypes.LE, // "<="
163 TokenTypes.LT, // '<'
164 TokenTypes.BXOR, // '^'
165 TokenTypes.BOR, // '|'
166 TokenTypes.LOR, // "||"
167 TokenTypes.BAND, // '&'
168 TokenTypes.LAND, // "&&"
169 TokenTypes.LITERAL_INSTANCEOF,
170 TokenTypes.TYPE_EXTENSION_AND,
171 TokenTypes.ASSIGN, // '='
172 TokenTypes.DIV_ASSIGN, // "/="
173 TokenTypes.PLUS_ASSIGN, // "+="
174 TokenTypes.MINUS_ASSIGN, //"-="
175 TokenTypes.STAR_ASSIGN, // "*="
176 TokenTypes.MOD_ASSIGN, // "%="
177 TokenTypes.SR_ASSIGN, // ">>="
178 TokenTypes.BSR_ASSIGN, // ">>>="
179 TokenTypes.SL_ASSIGN, // "<<="
180 TokenTypes.BXOR_ASSIGN, // "^="
181 TokenTypes.BOR_ASSIGN, // "|="
182 TokenTypes.BAND_ASSIGN, // "&="
183
184 };
185 }
186
187 @Override
188 public void visitToken(DetailAST ast)
189 {
190 if (ast.getType() == TokenTypes.COLON) {
191 final DetailAST parent = ast.getParent();
192 if ((parent.getType() == TokenTypes.LITERAL_DEFAULT)
193 || (parent.getType() == TokenTypes.LITERAL_CASE))
194 {
195 //we do not want to check colon for cases and defaults
196 return;
197 }
198 }
199 final WrapOption wOp = getAbstractOption();
200
201 final String text = ast.getText();
202 final int colNo = ast.getColumnNo();
203 final int lineNo = ast.getLineNo();
204 final String currentLine = getLine(lineNo - 1);
205
206 // TODO: Handle comments before and after operator
207 // Check if rest of line is whitespace, and not just the operator
208 // by itself. This last bit is to handle the operator on a line by
209 // itself.
210 if ((wOp == WrapOption.NL)
211 && !text.equals(currentLine.trim())
212 && (currentLine.substring(colNo + text.length())
213 .trim().length() == 0))
214 {
215 log(lineNo, colNo, LINE_NEW, text);
216 }
217 else if ((wOp == WrapOption.EOL)
218 && Utils.whitespaceBefore(colNo - 1, currentLine))
219 {
220 log(lineNo, colNo, LINE_PREVIOUS, text);
221 }
222 }
223 }