001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2015 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.whitespace;
021
022import org.apache.commons.lang3.ArrayUtils;
023import org.apache.commons.lang3.StringUtils;
024
025import com.puppycrawl.tools.checkstyle.api.DetailAST;
026import com.puppycrawl.tools.checkstyle.api.TokenTypes;
027import com.puppycrawl.tools.checkstyle.checks.AbstractOptionCheck;
028import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
029
030/**
031 * <p>
032 * Checks line wrapping for operators.
033 * The policy to verify is specified using the {@link WrapOption} class
034 * and defaults to {@link WrapOption#NL}.
035 * </p>
036 * <p> By default the check will check the following operators:
037 *  {@link TokenTypes#BAND BAND},
038 *  {@link TokenTypes#BOR BOR},
039 *  {@link TokenTypes#BSR BSR},
040 *  {@link TokenTypes#BXOR BXOR},
041 *  {@link TokenTypes#COLON COLON},
042 *  {@link TokenTypes#DIV DIV},
043 *  {@link TokenTypes#EQUAL EQUAL},
044 *  {@link TokenTypes#GE GE},
045 *  {@link TokenTypes#GT GT},
046 *  {@link TokenTypes#LAND LAND},
047 *  {@link TokenTypes#LE LE},
048 *  {@link TokenTypes#LITERAL_INSTANCEOF LITERAL_INSTANCEOF},
049 *  {@link TokenTypes#LOR LOR},
050 *  {@link TokenTypes#LT LT},
051 *  {@link TokenTypes#MINUS MINUS},
052 *  {@link TokenTypes#MOD MOD},
053 *  {@link TokenTypes#NOT_EQUAL NOT_EQUAL},
054 *  {@link TokenTypes#PLUS PLUS},
055 *  {@link TokenTypes#QUESTION QUESTION},
056 *  {@link TokenTypes#SL SL},
057 *  {@link TokenTypes#SR SR},
058 *  {@link TokenTypes#STAR STAR}.
059 * Other acceptable tokens are
060 *  {@link TokenTypes#ASSIGN ASSIGN},
061 *  {@link TokenTypes#BAND_ASSIGN BAND_ASSIGN},
062 *  {@link TokenTypes#BOR_ASSIGN BOR_ASSIGN},
063 *  {@link TokenTypes#BSR_ASSIGN BSR_ASSIGN},
064 *  {@link TokenTypes#BXOR_ASSIGN BXOR_ASSIGN},
065 *  {@link TokenTypes#DIV_ASSIGN DIV_ASSIGN},
066 *  {@link TokenTypes#MINUS_ASSIGN MINUS_ASSIGN},
067 *  {@link TokenTypes#MOD_ASSIGN MOD_ASSIGN},
068 *  {@link TokenTypes#PLUS_ASSIGN PLUS_ASSIGN},
069 *  {@link TokenTypes#SL_ASSIGN SL_ASSIGN},
070 *  {@link TokenTypes#SR_ASSIGN SR_ASSIGN},
071 *  {@link TokenTypes#STAR_ASSIGN STAR_ASSIGN}.
072 * </p>
073 *  <p>
074 * An example of how to configure the check is:
075 * </p>
076 * <pre>
077 * &lt;module name="OperatorWrap"/&gt;
078 * </pre>
079 * <p> An example of how to configure the check for assignment operators at the
080 * end of a line is:
081 * </p>
082 * <pre>
083 * &lt;module name="OperatorWrap"&gt;
084 *     &lt;property name="tokens"
085 *               value="ASSIGN,DIV_ASSIGN,PLUS_ASSIGN,MINUS_ASSIGN,STAR_ASSIGN,MOD_ASSIGN,SR_ASSIGN,BSR_ASSIGN,SL_ASSIGN,BXOR_ASSIGN,BOR_ASSIGN,BAND_ASSIGN"/&gt;
086 *     &lt;property name="option" value="eol"/&gt;
087  * &lt;/module&gt;
088 * </pre>
089 *
090 * @author Rick Giles
091 */
092public class OperatorWrapCheck
093    extends AbstractOptionCheck<WrapOption> {
094
095    /**
096     * A key is pointing to the warning message text in "messages.properties"
097     * file.
098     */
099    public static final String LINE_NEW = "line.new";
100
101    /**
102     * A key is pointing to the warning message text in "messages.properties"
103     * file.
104     */
105    public static final String LINE_PREVIOUS = "line.previous";
106
107    /**
108     * Sets the operator wrap option to new line.
109     */
110    public OperatorWrapCheck() {
111        super(WrapOption.NL, WrapOption.class);
112    }
113
114    @Override
115    public int[] getDefaultTokens() {
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        return new int[] {
146            TokenTypes.QUESTION,          // '?'
147            TokenTypes.COLON,             // ':' (not reported for a case)
148            TokenTypes.EQUAL,             // "=="
149            TokenTypes.NOT_EQUAL,         // "!="
150            TokenTypes.DIV,               // '/'
151            TokenTypes.PLUS,              //' +' (unary plus is UNARY_PLUS)
152            TokenTypes.MINUS,             // '-' (unary minus is UNARY_MINUS)
153            TokenTypes.STAR,              // '*'
154            TokenTypes.MOD,               // '%'
155            TokenTypes.SR,                // ">>"
156            TokenTypes.BSR,               // ">>>"
157            TokenTypes.GE,                // ">="
158            TokenTypes.GT,                // ">"
159            TokenTypes.SL,                // "<<"
160            TokenTypes.LE,                // "<="
161            TokenTypes.LT,                // '<'
162            TokenTypes.BXOR,              // '^'
163            TokenTypes.BOR,               // '|'
164            TokenTypes.LOR,               // "||"
165            TokenTypes.BAND,              // '&'
166            TokenTypes.LAND,              // "&&"
167            TokenTypes.LITERAL_INSTANCEOF,
168            TokenTypes.TYPE_EXTENSION_AND,
169            TokenTypes.ASSIGN,            // '='
170            TokenTypes.DIV_ASSIGN,        // "/="
171            TokenTypes.PLUS_ASSIGN,       // "+="
172            TokenTypes.MINUS_ASSIGN,      //"-="
173            TokenTypes.STAR_ASSIGN,       // "*="
174            TokenTypes.MOD_ASSIGN,        // "%="
175            TokenTypes.SR_ASSIGN,         // ">>="
176            TokenTypes.BSR_ASSIGN,        // ">>>="
177            TokenTypes.SL_ASSIGN,         // "<<="
178            TokenTypes.BXOR_ASSIGN,       // "^="
179            TokenTypes.BOR_ASSIGN,        // "|="
180            TokenTypes.BAND_ASSIGN,       // "&="
181
182        };
183    }
184
185    @Override
186    public int[] getRequiredTokens() {
187        return ArrayUtils.EMPTY_INT_ARRAY;
188    }
189
190    @Override
191    public void visitToken(DetailAST ast) {
192        if (ast.getType() == TokenTypes.COLON) {
193            final DetailAST parent = ast.getParent();
194            if (parent.getType() == TokenTypes.LITERAL_DEFAULT
195                || parent.getType() == TokenTypes.LITERAL_CASE) {
196                //we do not want to check colon for cases and defaults
197                return;
198            }
199        }
200        final WrapOption wOp = getAbstractOption();
201
202        final String text = ast.getText();
203        final int colNo = ast.getColumnNo();
204        final int lineNo = ast.getLineNo();
205        final String currentLine = getLine(lineNo - 1);
206
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                && StringUtils.isBlank(currentLine.substring(colNo + text.length()))) {
213            log(lineNo, colNo, LINE_NEW, text);
214        }
215        else if (wOp == WrapOption.EOL
216                && CommonUtils.hasWhitespaceBefore(colNo - 1, currentLine)) {
217            log(lineNo, colNo, LINE_PREVIOUS, text);
218        }
219    }
220}