001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2015 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.indentation; 021 022import java.util.ArrayDeque; 023import java.util.Deque; 024 025import com.puppycrawl.tools.checkstyle.api.Check; 026import com.puppycrawl.tools.checkstyle.api.DetailAST; 027 028/** 029 * Checks correct indentation of Java Code. 030 * 031 * <p> 032 * The basic idea behind this is that while 033 * pretty printers are sometimes convenient for reformats of 034 * legacy code, they often either aren't configurable enough or 035 * just can't anticipate how format should be done. Sometimes this is 036 * personal preference, other times it is practical experience. In any 037 * case, this check should just ensure that a minimal set of indentation 038 * rules are followed. 039 * </p> 040 * 041 * <p> 042 * Implementation -- 043 * Basically, this check requests visitation for all handled token 044 * types (those tokens registered in the HandlerFactory). When visitToken 045 * is called, a new ExpressionHandler is created for the AST and pushed 046 * onto the handlers stack. The new handler then checks the indentation 047 * for the currently visiting AST. When leaveToken is called, the 048 * ExpressionHandler is popped from the stack. 049 * </p> 050 * 051 * <p> 052 * While on the stack the ExpressionHandler can be queried for the 053 * indentation level it suggests for children as well as for other 054 * values. 055 * </p> 056 * 057 * <p> 058 * While an ExpressionHandler checks the indentation level of its own 059 * AST, it typically also checks surrounding ASTs. For instance, a 060 * while loop handler checks the while loop as well as the braces 061 * and immediate children. 062 * </p> 063 * <pre> 064 * - handler class -to-> ID mapping kept in Map 065 * - parent passed in during construction 066 * - suggest child indent level 067 * - allows for some tokens to be on same line (ie inner classes OBJBLOCK) 068 * and not increase indentation level 069 * - looked at using double dispatch for suggestedChildLevel(), but it 070 * doesn't seem worthwhile, at least now 071 * - both tabs and spaces are considered whitespace in front of the line... 072 * tabs are converted to spaces 073 * - block parents with parens -- for, while, if, etc... -- are checked that 074 * they match the level of the parent 075 * </pre> 076 * 077 * @author jrichard 078 * @author o_sukhodolsky 079 * @author Maikel Steneker 080 * @author maxvetrenko 081 */ 082public class IndentationCheck extends Check { 083 /** Default indentation amount - based on Sun. */ 084 private static final int DEFAULT_INDENTATION = 4; 085 086 /** How many tabs or spaces to use. */ 087 private int basicOffset = DEFAULT_INDENTATION; 088 089 /** How much to indent a case label. */ 090 private int caseIndent = DEFAULT_INDENTATION; 091 092 /** How far brace should be indented when on next line. */ 093 private int braceAdjustment; 094 095 /** How far throws should be indented when on next line. */ 096 private int throwsIndent = DEFAULT_INDENTATION; 097 098 /** How much to indent an array initialization when on next line. */ 099 private int arrayInitIndent = DEFAULT_INDENTATION; 100 101 /** How far continuation line should be indented when line-wrapping is present. */ 102 private int lineWrappingIndentation = DEFAULT_INDENTATION; 103 104 /** 105 * Force strict condition in line wrapping case. If value is true, line wrap indent 106 * have to be same as lineWrappingIndentation parameter, if value is false, line wrap indent 107 * have to be not less than lineWrappingIndentation parameter. 108 */ 109 private boolean forceStrictCondition; 110 111 /** Handlers currently in use. */ 112 private final Deque<AbstractExpressionHandler> handlers = new ArrayDeque<>(); 113 114 /** Factory from which handlers are distributed. */ 115 private final HandlerFactory handlerFactory = new HandlerFactory(); 116 117 /** 118 * Get forcing strict condition. 119 * @return forceStrictCondition value. 120 */ 121 public boolean isForceStrictCondition() { 122 return forceStrictCondition; 123 } 124 125 /** 126 * Set forcing strict condition. 127 * @param value user's value of forceStrictCondition. 128 */ 129 public void setForceStrictCondition(boolean value) { 130 forceStrictCondition = value; 131 } 132 133 /** 134 * Set the basic offset. 135 * 136 * @param basicOffset the number of tabs or spaces to indent 137 */ 138 public void setBasicOffset(int basicOffset) { 139 this.basicOffset = basicOffset; 140 } 141 142 /** 143 * Get the basic offset. 144 * 145 * @return the number of tabs or spaces to indent 146 */ 147 public int getBasicOffset() { 148 return basicOffset; 149 } 150 151 /** 152 * Adjusts brace indentation (positive offset). 153 * 154 * @param adjustmentAmount the brace offset 155 */ 156 public void setBraceAdjustment(int adjustmentAmount) { 157 braceAdjustment = adjustmentAmount; 158 } 159 160 /** 161 * Get the brace adjustment amount. 162 * 163 * @return the positive offset to adjust braces 164 */ 165 public int getBraceAdjustment() { 166 return braceAdjustment; 167 } 168 169 /** 170 * Set the case indentation level. 171 * 172 * @param amount the case indentation level 173 */ 174 public void setCaseIndent(int amount) { 175 caseIndent = amount; 176 } 177 178 /** 179 * Get the case indentation level. 180 * 181 * @return the case indentation level 182 */ 183 public int getCaseIndent() { 184 return caseIndent; 185 } 186 187 /** 188 * Set the throws indentation level. 189 * 190 * @param throwsIndent the throws indentation level 191 */ 192 public void setThrowsIndent(int throwsIndent) { 193 this.throwsIndent = throwsIndent; 194 } 195 196 /** 197 * Get the throws indentation level. 198 * 199 * @return the throws indentation level 200 */ 201 public int getThrowsIndent() { 202 return throwsIndent; 203 } 204 205 /** 206 * Set the array initialisation indentation level. 207 * 208 * @param arrayInitIndent the array initialisation indentation level 209 */ 210 public void setArrayInitIndent(int arrayInitIndent) { 211 this.arrayInitIndent = arrayInitIndent; 212 } 213 214 /** 215 * Get the line-wrapping indentation level. 216 * 217 * @return the initialisation indentation level 218 */ 219 public int getArrayInitIndent() { 220 return arrayInitIndent; 221 } 222 223 /** 224 * Get the array line-wrapping indentation level. 225 * 226 * @return the line-wrapping indentation level 227 */ 228 public int getLineWrappingIndentation() { 229 return lineWrappingIndentation; 230 } 231 232 /** 233 * Set the line-wrapping indentation level. 234 * 235 * @param lineWrappingIndentation the line-wrapping indentation level 236 */ 237 public void setLineWrappingIndentation(int lineWrappingIndentation) { 238 this.lineWrappingIndentation = lineWrappingIndentation; 239 } 240 241 /** 242 * Log an error message. 243 * 244 * @param line the line number where the error was found 245 * @param key the message that describes the error 246 * @param args the details of the message 247 * 248 * @see java.text.MessageFormat 249 */ 250 public void indentationLog(int line, String key, Object... args) { 251 log(line, key, args); 252 } 253 254 /** 255 * Get the width of a tab. 256 * 257 * @return the width of a tab 258 */ 259 public int getIndentationTabWidth() { 260 return getTabWidth(); 261 } 262 263 @Override 264 public int[] getDefaultTokens() { 265 return getAcceptableTokens(); 266 } 267 268 @Override 269 public int[] getAcceptableTokens() { 270 return handlerFactory.getHandledTypes(); 271 } 272 273 @Override 274 public int[] getRequiredTokens() { 275 return getAcceptableTokens(); 276 } 277 278 @Override 279 public void beginTree(DetailAST ast) { 280 handlerFactory.clearCreatedHandlers(); 281 handlers.clear(); 282 final PrimordialHandler primordialHandler = new PrimordialHandler(this); 283 handlers.push(primordialHandler); 284 primordialHandler.checkIndentation(); 285 } 286 287 @Override 288 public void visitToken(DetailAST ast) { 289 final AbstractExpressionHandler handler = handlerFactory.getHandler(this, ast, 290 handlers.peek()); 291 handlers.push(handler); 292 handler.checkIndentation(); 293 } 294 295 @Override 296 public void leaveToken(DetailAST ast) { 297 handlers.pop(); 298 } 299 300 /** 301 * Accessor for the handler factory. 302 * 303 * @return the handler factory 304 */ 305 final HandlerFactory getHandlerFactory() { 306 return handlerFactory; 307 } 308}