001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2014  Oliver Burn
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 com.puppycrawl.tools.checkstyle.api.DetailAST;
023import com.puppycrawl.tools.checkstyle.api.TokenTypes;
024import com.puppycrawl.tools.checkstyle.api.Utils;
025import com.puppycrawl.tools.checkstyle.checks.AbstractOptionCheck;
026
027/**
028 * <p>
029 * Checks line wrapping for operators.
030 * The policy to verify is specified using the {@link WrapOption} class
031 * and defaults to {@link WrapOption#NL}.
032 * </p>
033 * <p> By default the check will check the following operators:
034 *  {@link TokenTypes#BAND BAND},
035 *  {@link TokenTypes#BOR BOR},
036 *  {@link TokenTypes#BSR BSR},
037 *  {@link TokenTypes#BXOR BXOR},
038 *  {@link TokenTypes#COLON COLON},
039 *  {@link TokenTypes#DIV DIV},
040 *  {@link TokenTypes#EQUAL EQUAL},
041 *  {@link TokenTypes#GE GE},
042 *  {@link TokenTypes#GT GT},
043 *  {@link TokenTypes#LAND LAND},
044 *  {@link TokenTypes#LE LE},
045 *  {@link TokenTypes#LITERAL_INSTANCEOF LITERAL_INSTANCEOF},
046 *  {@link TokenTypes#LOR LOR},
047 *  {@link TokenTypes#LT LT},
048 *  {@link TokenTypes#MINUS MINUS},
049 *  {@link TokenTypes#MOD MOD},
050 *  {@link TokenTypes#NOT_EQUAL NOT_EQUAL},
051 *  {@link TokenTypes#PLUS PLUS},
052 *  {@link TokenTypes#QUESTION QUESTION},
053 *  {@link TokenTypes#SL SL},
054 *  {@link TokenTypes#SR SR},
055 *  {@link TokenTypes#STAR STAR}.
056 * Other acceptable tokens are
057 *  {@link TokenTypes#ASSIGN ASSIGN},
058 *  {@link TokenTypes#BAND_ASSIGN BAND_ASSIGN},
059 *  {@link TokenTypes#BOR_ASSIGN BOR_ASSIGN},
060 *  {@link TokenTypes#BSR_ASSIGN BSR_ASSIGN},
061 *  {@link TokenTypes#BXOR_ASSIGN BXOR_ASSIGN},
062 *  {@link TokenTypes#DIV_ASSIGN DIV_ASSIGN},
063 *  {@link TokenTypes#MINUS_ASSIGN MINUS_ASSIGN},
064 *  {@link TokenTypes#MOD_ASSIGN MOD_ASSIGN},
065 *  {@link TokenTypes#PLUS_ASSIGN PLUS_ASSIGN},
066 *  {@link TokenTypes#SL_ASSIGN SL_ASSIGN},
067 *  {@link TokenTypes#SR_ASSIGN SR_ASSIGN},
068 *  {@link TokenTypes#STAR_ASSIGN STAR_ASSIGN}.
069 * </p>
070 *  <p>
071 * An example of how to configure the check is:
072 * </p>
073 * <pre>
074 * &lt;module name="OperatorWrap"/&gt;
075 * </pre>
076 * <p> An example of how to configure the check for assignment operators at the
077 * end of a line is:
078 * </p>
079 * <pre>
080 * &lt;module name="OperatorWrap"&gt;
081 *     &lt;property name="tokens"
082 *               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;
083 *     &lt;property name="option" value="eol"/&gt;
084  * &lt;/module&gt;
085 * </pre>
086 *
087 * @author Rick Giles
088 * @version 1.0
089 */
090public class OperatorWrapCheck
091    extends AbstractOptionCheck<WrapOption>
092{
093    /**
094     * Sets the operator wrap option to new line.
095     */
096    public OperatorWrapCheck()
097    {
098        super(WrapOption.NL, WrapOption.class);
099    }
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}