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.Check; 023import com.puppycrawl.tools.checkstyle.api.DetailAST; 024import com.puppycrawl.tools.checkstyle.api.TokenTypes; 025 026/** 027 * 028 * Checks for empty line separators after header, package, all import declarations, 029 * fields, constructors, methods, nested classes, 030 * static initializers and instance initializers. 031 * 032 * <p> By default the check will check the following statements: 033 * {@link TokenTypes#PACKAGE_DEF PACKAGE_DEF}, 034 * {@link TokenTypes#IMPORT IMPORT}, 035 * {@link TokenTypes#CLASS_DEF CLASS_DEF}, 036 * {@link TokenTypes#INTERFACE_DEF INTERFACE_DEF}, 037 * {@link TokenTypes#STATIC_INIT STATIC_INIT}, 038 * {@link TokenTypes#INSTANCE_INIT INSTANCE_INIT}, 039 * {@link TokenTypes#METHOD_DEF METHOD_DEF}, 040 * {@link TokenTypes#CTOR_DEF CTOR_DEF}, 041 * {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF}. 042 * </p> 043 * 044 * <p> 045 * Example of declarations without empty line separator: 046 * </p> 047 * 048 * <pre> 049 * /////////////////////////////////////////////////// 050 * //HEADER 051 * /////////////////////////////////////////////////// 052 * package com.puppycrawl.tools.checkstyle.whitespace; 053 * import java.io.Serializable; 054 * class Foo 055 * { 056 * public static final int FOO_CONST = 1; 057 * public void foo() {} //should be separated from previous statement. 058 * } 059 * </pre> 060 * 061 * <p> An example of how to configure the check with default parameters is: 062 * </p> 063 * 064 * <pre> 065 * <module name="EmptyLineSeparator"/> 066 * </pre> 067 * 068 * <p> 069 * Example of declarations with empty line separator 070 * that is expected by the Check by default: 071 * </p> 072 * 073 * <pre> 074 * /////////////////////////////////////////////////// 075 * //HEADER 076 * /////////////////////////////////////////////////// 077 * 078 * package com.puppycrawl.tools.checkstyle.whitespace; 079 * 080 * import java.io.Serializable; 081 * 082 * class Foo 083 * { 084 * public static final int FOO_CONST = 1; 085 * 086 * public void foo() {} 087 * } 088 * </pre> 089 * <p> An example how to check empty line after 090 * {@link TokenTypes#VARIABLE_DEF VARIABLE_DEF} and 091 * {@link TokenTypes#METHOD_DEF METHOD_DEF}: 092 * </p> 093 * 094 * <pre> 095 * <module name="EmptyLineSeparator"> 096 * <property name="tokens" value="VARIABLE_DEF, METHOD_DEF"/> 097 * </module> 098 * </pre> 099 * 100 * <p> 101 * An example how to allow no empty line between fields: 102 * </p> 103 * <pre> 104 * <module name="EmptyLineSeparator"> 105 * <property name="allowNoEmptyLineBetweenFields" value="true"/> 106 * </module> 107 * </pre> 108 * 109 * @author maxvetrenko 110 * 111 */ 112public class EmptyLineSeparatorCheck extends Check 113{ 114 /** */ 115 private boolean mAllowNoEmptyLineBetweenFields; 116 117 /** 118 * Allow no empty line between fields. 119 * @param aAllow 120 * User's value. 121 */ 122 public final void setAllowNoEmptyLineBetweenFields(boolean aAllow) 123 { 124 mAllowNoEmptyLineBetweenFields = aAllow; 125 } 126 127 @Override 128 public int[] getDefaultTokens() 129 { 130 return new int[] { 131 TokenTypes.PACKAGE_DEF, 132 TokenTypes.IMPORT, 133 TokenTypes.CLASS_DEF, 134 TokenTypes.INTERFACE_DEF, 135 TokenTypes.ENUM_DEF, 136 TokenTypes.STATIC_INIT, 137 TokenTypes.INSTANCE_INIT, 138 TokenTypes.METHOD_DEF, 139 TokenTypes.CTOR_DEF, 140 TokenTypes.VARIABLE_DEF, 141 }; 142 } 143 144 @Override 145 public void visitToken(DetailAST aAST) 146 { 147 final DetailAST nextToken = aAST.getNextSibling(); 148 149 if (nextToken != null && nextToken.getType() != TokenTypes.RCURLY) { 150 final int astType = aAST.getType(); 151 switch (astType) { 152 case TokenTypes.VARIABLE_DEF: 153 if (isTypeField(aAST) && !hasEmptyLineAfter(aAST)) { 154 if (mAllowNoEmptyLineBetweenFields 155 && nextToken.getType() != TokenTypes.VARIABLE_DEF) 156 { 157 log(nextToken.getLineNo(), "empty.line.separator", nextToken.getText()); 158 } 159 else if (!mAllowNoEmptyLineBetweenFields) { 160 log(nextToken.getLineNo(), "empty.line.separator", nextToken.getText()); 161 } 162 } 163 break; 164 case TokenTypes.IMPORT: 165 if (astType != nextToken.getType() && !hasEmptyLineAfter(aAST) 166 || (aAST.getLineNo() > 1 && !hasEmptyLineBefore(aAST) 167 && aAST.getPreviousSibling() == null)) 168 { 169 log(nextToken.getLineNo(), "empty.line.separator", nextToken.getText()); 170 } 171 break; 172 case TokenTypes.PACKAGE_DEF: 173 if (aAST.getLineNo() > 1 && !hasEmptyLineBefore(aAST)) { 174 log(aAST.getLineNo(), "empty.line.separator", aAST.getText()); 175 } 176 default: 177 if (!hasEmptyLineAfter(aAST)) { 178 log(nextToken.getLineNo(), "empty.line.separator", nextToken.getText()); 179 } 180 } 181 } 182 } 183 184 /** 185 * Checks if token have empty line after. 186 * @param aToken token. 187 * @return true if token have empty line after. 188 */ 189 private boolean hasEmptyLineAfter(DetailAST aToken) 190 { 191 DetailAST lastToken = aToken.getLastChild().getLastChild(); 192 if (null == lastToken) { 193 lastToken = aToken.getLastChild(); 194 } 195 return aToken.getNextSibling().getLineNo() - lastToken.getLineNo() > 1; 196 } 197 198 /** 199 * Checks if a token has a empty line before. 200 * @param aToken token. 201 * @return true, if token have empty line before. 202 */ 203 private boolean hasEmptyLineBefore(DetailAST aToken) 204 { 205 final int lineNo = aToken.getLineNo(); 206 // [lineNo - 2] is the number of the previous line because the numbering starts from zero. 207 final String lineBefore = getLines()[lineNo - 2]; 208 return lineBefore.trim().isEmpty(); 209 } 210 211 /** 212 * If variable definition is a type field. 213 * @param aVariableDef variable definition. 214 * @return true variable definition is a type field. 215 */ 216 private boolean isTypeField(DetailAST aVariableDef) 217 { 218 final int parentType = aVariableDef.getParent().getParent().getType(); 219 return parentType == TokenTypes.CLASS_DEF; 220 } 221}