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//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.blocks; 020 021import com.puppycrawl.tools.checkstyle.api.DetailAST; 022import com.puppycrawl.tools.checkstyle.api.TokenTypes; 023import com.puppycrawl.tools.checkstyle.api.Utils; 024import com.puppycrawl.tools.checkstyle.checks.AbstractOptionCheck; 025import com.puppycrawl.tools.checkstyle.checks.CheckUtils; 026 027/** 028 * <p> 029 * Checks the placement of right curly braces. 030 * The policy to verify is specified using the {@link RightCurlyOption} class 031 * and defaults to {@link RightCurlyOption#SAME}. 032 * </p> 033 * <p> By default the check will check the following tokens: 034 * {@link TokenTypes#LITERAL_TRY LITERAL_TRY}, 035 * {@link TokenTypes#LITERAL_CATCH LITERAL_CATCH}, 036 * {@link TokenTypes#LITERAL_FINALLY LITERAL_FINALLY}, 037 * {@link TokenTypes#LITERAL_IF LITERAL_IF}, 038 * {@link TokenTypes#LITERAL_ELSE LITERAL_ELSE}. 039 * Other acceptable tokens are: 040 * {@link TokenTypes#CLASS_DEF CLASS_DEF}, 041 * {@link TokenTypes#METHOD_DEF METHOD_DEF}, 042 * {@link TokenTypes#CTOR_DEF CTOR_DEF}. 043 * {@link TokenTypes#LITERAL_FOR LITERAL_FOR}. 044 * {@link TokenTypes#LITERAL_WHILE LITERAL_WHILE}. 045 * {@link TokenTypes#LITERAL_DO LITERAL_DO}. 046 * {@link TokenTypes#STATIC_INIT STATIC_INIT}. 047 * {@link TokenTypes#INSTANCE_INIT INSTANCE_INIT}. 048 * </p> 049 * <p> 050 * An example of how to configure the check is: 051 * </p> 052 * <pre> 053 * <module name="RightCurly"/> 054 * </pre> 055 * <p> 056 * An example of how to configure the check with policy 057 * {@link RightCurlyOption#ALONE} for <code>else</code> and 058 * <code>{@link TokenTypes#METHOD_DEF METHOD_DEF}</code>tokens is: 059 * </p> 060 * <pre> 061 * <module name="RightCurly"> 062 * <property name="tokens" value="LITERAL_ELSE"/> 063 * <property name="option" value="alone"/> 064 * </module> 065 * </pre> 066 * 067 * @author Oliver Burn 068 * @author lkuehne 069 * @author o_sukhodolsky 070 * @author maxvetrenko 071 * @version 2.0 072 */ 073public class RightCurlyCheck extends AbstractOptionCheck<RightCurlyOption> 074{ 075 /** Do we need to check if rculry starts line. */ 076 private boolean mShouldStartLine = true; 077 078 /** 079 * Sets the right curly option to same. 080 */ 081 public RightCurlyCheck() 082 { 083 super(RightCurlyOption.SAME, RightCurlyOption.class); 084 } 085 086 /** 087 * Does the check need to check if rcurly starts line. 088 * @param aFlag new value of this property. 089 */ 090 public void setShouldStartLine(boolean aFlag) 091 { 092 mShouldStartLine = aFlag; 093 } 094 095 @Override 096 public int[] getDefaultTokens() 097 { 098 return new int[] { 099 TokenTypes.LITERAL_TRY, 100 TokenTypes.LITERAL_CATCH, 101 TokenTypes.LITERAL_FINALLY, 102 TokenTypes.LITERAL_IF, 103 TokenTypes.LITERAL_ELSE, 104 }; 105 } 106 107 @Override 108 public int[] getAcceptableTokens() 109 { 110 return new int[] { 111 TokenTypes.LITERAL_TRY, 112 TokenTypes.LITERAL_CATCH, 113 TokenTypes.LITERAL_FINALLY, 114 TokenTypes.LITERAL_IF, 115 TokenTypes.LITERAL_ELSE, 116 TokenTypes.CLASS_DEF, 117 TokenTypes.METHOD_DEF, 118 TokenTypes.CTOR_DEF, 119 TokenTypes.LITERAL_FOR, 120 TokenTypes.LITERAL_WHILE, 121 TokenTypes.LITERAL_DO, 122 TokenTypes.STATIC_INIT, 123 TokenTypes.INSTANCE_INIT, 124 }; 125 } 126 127 @Override 128 public void visitToken(DetailAST aAST) 129 { 130 // Attempt to locate the tokens to do the check 131 DetailAST rcurly; 132 DetailAST lcurly; 133 DetailAST nextToken; 134 boolean shouldCheckLastRcurly = false; 135 136 switch (aAST.getType()) { 137 case TokenTypes.LITERAL_TRY: 138 lcurly = aAST.getFirstChild(); 139 nextToken = lcurly.getNextSibling(); 140 rcurly = lcurly.getLastChild(); 141 break; 142 case TokenTypes.LITERAL_CATCH: 143 nextToken = aAST.getNextSibling(); 144 lcurly = aAST.getLastChild(); 145 rcurly = lcurly.getLastChild(); 146 if (nextToken == null) { 147 shouldCheckLastRcurly = true; 148 nextToken = getNextToken(aAST); 149 } 150 break; 151 case TokenTypes.LITERAL_IF: 152 nextToken = aAST.findFirstToken(TokenTypes.LITERAL_ELSE); 153 if (nextToken != null) { 154 lcurly = nextToken.getPreviousSibling(); 155 rcurly = lcurly.getLastChild(); 156 } 157 else { 158 shouldCheckLastRcurly = true; 159 nextToken = getNextToken(aAST); 160 lcurly = aAST.getLastChild(); 161 rcurly = lcurly.getLastChild(); 162 } 163 break; 164 case TokenTypes.LITERAL_ELSE: 165 shouldCheckLastRcurly = true; 166 nextToken = getNextToken(aAST); 167 lcurly = aAST.getFirstChild(); 168 rcurly = lcurly.getLastChild(); 169 break; 170 case TokenTypes.LITERAL_FINALLY: 171 shouldCheckLastRcurly = true; 172 nextToken = getNextToken(aAST); 173 lcurly = aAST.getFirstChild(); 174 rcurly = lcurly.getLastChild(); 175 break; 176 case TokenTypes.CLASS_DEF: 177 final DetailAST child = aAST.getLastChild(); 178 lcurly = child.getFirstChild(); 179 rcurly = child.getLastChild(); 180 nextToken = aAST; 181 break; 182 case TokenTypes.CTOR_DEF: 183 case TokenTypes.STATIC_INIT: 184 case TokenTypes.INSTANCE_INIT: 185 lcurly = aAST.findFirstToken(TokenTypes.SLIST); 186 rcurly = lcurly.getLastChild(); 187 nextToken = aAST; 188 break; 189 case TokenTypes.METHOD_DEF: 190 case TokenTypes.LITERAL_FOR: 191 case TokenTypes.LITERAL_WHILE: 192 case TokenTypes.LITERAL_DO: 193 lcurly = aAST.findFirstToken(TokenTypes.SLIST); 194 //SLIST could be absent if method is abstract, and code like "while(true);" 195 if (lcurly == null) { 196 return; 197 } 198 rcurly = lcurly.getLastChild(); 199 nextToken = aAST; 200 break; 201 default: 202 throw new RuntimeException("Unexpected token type (" 203 + TokenTypes.getTokenName(aAST.getType()) + ")"); 204 } 205 206 if ((rcurly == null) || (rcurly.getType() != TokenTypes.RCURLY)) { 207 // we need to have both tokens to perform the check 208 return; 209 } 210 211 if (getAbstractOption() == RightCurlyOption.SAME && !hasLineBreakBefore(rcurly)) { 212 log(rcurly, "line.break.before"); 213 } 214 215 if (shouldCheckLastRcurly) { 216 if (rcurly.getLineNo() == nextToken.getLineNo()) { 217 log(rcurly, "line.alone", "}"); 218 } 219 } 220 else if ((getAbstractOption() == RightCurlyOption.SAME) 221 && (rcurly.getLineNo() != nextToken.getLineNo())) 222 { 223 log(rcurly, "line.same", "}"); 224 } 225 else if ((getAbstractOption() == RightCurlyOption.ALONE) 226 && (rcurly.getLineNo() == nextToken.getLineNo()) 227 && !isEmptyBody(lcurly)) 228 { 229 log(rcurly, "line.alone", "}"); 230 } 231 232 if (!mShouldStartLine) { 233 return; 234 } 235 final boolean startsLine = 236 Utils.whitespaceBefore(rcurly.getColumnNo(), 237 getLines()[rcurly.getLineNo() - 1]); 238 239 if (!startsLine && (lcurly.getLineNo() != rcurly.getLineNo())) { 240 log(rcurly, "line.new", "}"); 241 } 242 } 243 244 /** 245 * Checks if definition body is empty. 246 * @param aLcurly left curly. 247 * @return true if definition body is empty. 248 */ 249 private boolean isEmptyBody(DetailAST aLcurly) 250 { 251 boolean result = false; 252 if (aLcurly.getParent().getType() == TokenTypes.OBJBLOCK) { 253 if (aLcurly.getNextSibling().getType() == TokenTypes.RCURLY) { 254 result = true; 255 } 256 } 257 else if (aLcurly.getFirstChild().getType() == TokenTypes.RCURLY) { 258 result = true; 259 } 260 return result; 261 } 262 263 /** 264 * Finds next token after the given one. 265 * @param aAST the given node. 266 * @return the token which represents next lexical item. 267 */ 268 private DetailAST getNextToken(DetailAST aAST) 269 { 270 DetailAST next = null; 271 DetailAST parent = aAST; 272 while ((parent != null) && (next == null)) { 273 next = parent.getNextSibling(); 274 parent = parent.getParent(); 275 } 276 return CheckUtils.getFirstNode(next); 277 } 278 279 /** 280 * Checks if right curly has line break before. 281 * @param aRightCurly 282 * Right curly token. 283 * @return 284 * True, if right curly has line break before. 285 */ 286 private boolean hasLineBreakBefore(DetailAST aRightCurly) 287 { 288 if (aRightCurly != null) { 289 final DetailAST previousToken = aRightCurly.getPreviousSibling(); 290 if (previousToken != null && aRightCurly.getLineNo() == previousToken.getLineNo()) { 291 return false; 292 } 293 } 294 return true; 295 } 296}