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.coding; 020 021import com.puppycrawl.tools.checkstyle.api.Check; 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.TokenTypes; 024 025/** 026 * Restricts the number of statements per line to one. 027 * @author Alexander Jesse 028 * @author Oliver Burn 029 */ 030public final class OneStatementPerLineCheck extends Check 031{ 032 /** hold the line-number where the last statement ended. */ 033 private int mLastStatementEnd = -1; 034 /** tracks the depth of EXPR tokens. */ 035 private int mExprDepth; 036 037 /** 038 * The for-header usually has 3 statements on one line, but THIS IS OK. 039 */ 040 private boolean mInForHeader; 041 042 @Override 043 public int[] getDefaultTokens() 044 { 045 return new int[] { 046 TokenTypes.EXPR, TokenTypes.SEMI, TokenTypes.FOR_INIT, 047 TokenTypes.FOR_ITERATOR, 048 }; 049 } 050 051 @Override 052 public void beginTree(DetailAST aRootAST) 053 { 054 mExprDepth = 0; 055 mInForHeader = false; 056 mLastStatementEnd = -1; 057 } 058 059 @Override 060 public void visitToken(DetailAST aAst) 061 { 062 switch (aAst.getType()) { 063 case TokenTypes.EXPR: 064 visitExpr(aAst); 065 break; 066 case TokenTypes.SEMI: 067 visitSemi(aAst); 068 break; 069 case TokenTypes.FOR_INIT: 070 mInForHeader = true; 071 break; 072 default: 073 break; 074 } 075 } 076 077 @Override 078 public void leaveToken(DetailAST aAst) 079 { 080 switch (aAst.getType()) { 081 case TokenTypes.FOR_ITERATOR: 082 mInForHeader = false; 083 break; 084 case TokenTypes.EXPR: 085 mExprDepth--; 086 break; 087 default: 088 break; 089 } 090 } 091 092 /** 093 * Mark the state-change for the statement (entering) and remember the 094 * first line of the last statement. If the first line of the new 095 * statement is the same as the last line of the last statement and we are 096 * not within a for-statement, then the rule is violated. 097 * @param aAst token for the {@link TokenTypes#EXPR}. 098 */ 099 private void visitExpr(DetailAST aAst) 100 { 101 mExprDepth++; 102 if (mExprDepth == 1 103 && !mInForHeader 104 && (mLastStatementEnd == aAst.getLineNo())) 105 { 106 log(aAst, "multiple.statements.line"); 107 } 108 } 109 110 /** 111 * Mark the state-change for the statement (leaving) and remember the last 112 * line of the last statement. 113 * @param aAst for the {@link TokenTypes#SEMI}. 114 */ 115 private void visitSemi(DetailAST aAst) 116 { 117 if (mExprDepth == 0) { 118 mLastStatementEnd = aAst.getLineNo(); 119 } 120 } 121}