001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 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 java.util.Locale;
023
024import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
025import com.puppycrawl.tools.checkstyle.api.DetailAST;
026import com.puppycrawl.tools.checkstyle.api.TokenTypes;
027import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
028
029/**
030 * <p>
031 * Checks line wrapping with separators.
032 * The policy to verify is specified using the {@link WrapOption} class
033 * and defaults to {@link WrapOption#EOL}.
034 * </p>
035 * <p> By default the check will check the following separators:
036 *  {@link TokenTypes#DOT DOT},
037 *  {@link TokenTypes#COMMA COMMA},
038 * Other acceptable tokens are
039 *  {@link TokenTypes#SEMI SEMI},
040 *  {@link TokenTypes#ELLIPSIS ELLIPSIS},
041 *  {@link TokenTypes#AT AT},
042 *  {@link TokenTypes#LPAREN LPAREN},
043 *  {@link TokenTypes#RPAREN RPAREN},
044 *  {@link TokenTypes#ARRAY_DECLARATOR ARRAY_DECLARATOR},
045 *  {@link TokenTypes#RBRACK RBRACK},
046 * </p>
047 * <p>
048 * Code example for comma and dot at the new line:
049 * </p>
050 * <pre>
051 * s
052 *    .isEmpty();
053 * foo(i
054 *    ,s);
055 * </pre>
056 *  <p>
057 * An example of how to configure the check is:
058 * </p>
059 * <pre>
060 * &lt;module name="SeparatorWrap"/&gt;
061 * </pre>
062 * <p>
063 * Code example for comma and dot at the previous line:
064 * </p>
065 * <pre>
066 * s.
067 *    isEmpty();
068 * foo(i,
069 *    s);
070 * </pre>
071 * <p> An example of how to configure the check for comma at the
072 * new line is:
073 * </p>
074 * <pre>
075 * &lt;module name="SeparatorWrap"&gt;
076 *     &lt;property name="tokens" value="COMMA"/&gt;
077 *     &lt;property name="option" value="nl"/&gt;
078 * &lt;/module&gt;
079 * </pre>
080 *
081 * @author maxvetrenko
082 */
083public class SeparatorWrapCheck
084    extends AbstractCheck {
085
086    /**
087     * A key is pointing to the warning message text in "messages.properties"
088     * file.
089     */
090    public static final String MSG_LINE_PREVIOUS = "line.previous";
091
092    /**
093     * A key is pointing to the warning message text in "messages.properties"
094     * file.
095     */
096    public static final String MSG_LINE_NEW = "line.new";
097
098    /** The policy to enforce. */
099    private WrapOption option = WrapOption.EOL;
100
101    /**
102     * Set the option to enforce.
103     * @param optionStr string to decode option from
104     * @throws IllegalArgumentException if unable to decode
105     */
106    public void setOption(String optionStr) {
107        try {
108            option = WrapOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
109        }
110        catch (IllegalArgumentException iae) {
111            throw new IllegalArgumentException("unable to parse " + optionStr, iae);
112        }
113    }
114
115    @Override
116    public int[] getDefaultTokens() {
117        return new int[] {
118            TokenTypes.DOT,
119            TokenTypes.COMMA,
120        };
121    }
122
123    @Override
124    public int[] getAcceptableTokens() {
125        return new int[] {
126            TokenTypes.DOT,
127            TokenTypes.COMMA,
128            TokenTypes.SEMI,
129            TokenTypes.ELLIPSIS,
130            TokenTypes.AT,
131            TokenTypes.LPAREN,
132            TokenTypes.RPAREN,
133            TokenTypes.ARRAY_DECLARATOR,
134            TokenTypes.RBRACK,
135            TokenTypes.METHOD_REF,
136        };
137    }
138
139    @Override
140    public int[] getRequiredTokens() {
141        return CommonUtils.EMPTY_INT_ARRAY;
142    }
143
144    @Override
145    public void visitToken(DetailAST ast) {
146        final String text = ast.getText();
147        final int colNo = ast.getColumnNo();
148        final int lineNo = ast.getLineNo();
149        final String currentLine = getLines()[lineNo - 1];
150        final String substringAfterToken =
151                currentLine.substring(colNo + text.length()).trim();
152        final String substringBeforeToken =
153                currentLine.substring(0, colNo).trim();
154
155        if (option == WrapOption.EOL
156                && substringBeforeToken.isEmpty()) {
157            log(lineNo, colNo, MSG_LINE_PREVIOUS, text);
158        }
159        else if (option == WrapOption.NL
160                 && substringAfterToken.isEmpty()) {
161            log(lineNo, colNo, MSG_LINE_NEW, text);
162        }
163    }
164}