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 lastStatementEnd = -1; 034 /** tracks the depth of EXPR tokens. */ 035 private int exprDepth; 036 037 /** 038 * The for-header usually has 3 statements on one line, but THIS IS OK. 039 */ 040 private boolean inForHeader; 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 rootAST) 053 { 054 exprDepth = 0; 055 inForHeader = false; 056 lastStatementEnd = -1; 057 } 058 059 @Override 060 public void visitToken(DetailAST ast) 061 { 062 switch (ast.getType()) { 063 case TokenTypes.EXPR: 064 visitExpr(ast); 065 break; 066 case TokenTypes.SEMI: 067 visitSemi(ast); 068 break; 069 case TokenTypes.FOR_INIT: 070 inForHeader = true; 071 break; 072 default: 073 break; 074 } 075 } 076 077 @Override 078 public void leaveToken(DetailAST ast) 079 { 080 switch (ast.getType()) { 081 case TokenTypes.FOR_ITERATOR: 082 inForHeader = false; 083 break; 084 case TokenTypes.EXPR: 085 exprDepth--; 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 ast token for the {@link TokenTypes#EXPR}. 098 */ 099 private void visitExpr(DetailAST ast) 100 { 101 exprDepth++; 102 if (exprDepth == 1 103 && !inForHeader 104 && (lastStatementEnd == ast.getLineNo())) 105 { 106 log(ast, "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 ast for the {@link TokenTypes#SEMI}. 114 */ 115 private void visitSemi(DetailAST ast) 116 { 117 if (exprDepth == 0) { 118 lastStatementEnd = ast.getLineNo(); 119 } 120 } 121}