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