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.indentation; 020 021import com.puppycrawl.tools.checkstyle.api.Check; 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.FastStack; 024 025// TODO: allow preset indentation styles (IE... GNU style, Sun style, etc...)? 026 027// TODO: optionally make imports (and other?) statements required to start 028// line? -- but maybe this should be a different check 029 030// TODO: optionally allow array children, throws clause, etc... 031// to be of any indentation > required, for emacs-style indentation 032 033// TODO: this is not illegal, but probably should be: 034// myfunc3(11, 11, Integer. 035// getInteger("mytest").intValue(), // this should be in 4 more 036// 11); 037 038// TODO: any dot-based indentation doesn't work (at least not yet...) the 039// problem is that we don't know which way an expression tree will be built 040// and with dot trees, they are built backwards. This means code like 041// 042// org.blah.mystuff 043// .myclass.getFactoryObject() 044// .objFunc().otherMethod(); 045// and 046// return ((MethodCallHandler) parent) 047// .findContainingMethodCall(this); 048// is all checked at the level of the first line. Simple dots are actually 049// checked but the method call handler will have to be changed drastically 050// to fix the above... 051 052 053/** 054 * Checks correct indentation of Java Code. 055 * 056 * <p> 057 * The basic idea behind this is that while 058 * pretty printers are sometimes convenient for bulk reformats of 059 * legacy code, they often either aren't configurable enough or 060 * just can't anticipate how format should be done. Sometimes this is 061 * personal preference, other times it is practical experience. In any 062 * case, this check should just ensure that a minimal set of indentation 063 * rules are followed. 064 * </p> 065 * 066 * <p> 067 * Implementation -- 068 * Basically, this check requests visitation for all handled token 069 * types (those tokens registered in the HandlerFactory). When visitToken 070 * is called, a new ExpressionHandler is created for the AST and pushed 071 * onto the mHandlers stack. The new handler then checks the indentation 072 * for the currently visiting AST. When leaveToken is called, the 073 * ExpressionHandler is popped from the stack. 074 * </p> 075 * 076 * <p> 077 * While on the stack the ExpressionHandler can be queried for the 078 * indentation level it suggests for children as well as for other 079 * values. 080 * </p> 081 * 082 * <p> 083 * While an ExpressionHandler checks the indentation level of its own 084 * AST, it typically also checks surrounding ASTs. For instance, a 085 * while loop handler checks the while loop as well as the braces 086 * and immediate children. 087 * </p> 088 * <pre> 089 * - handler class -to-> ID mapping kept in Map 090 * - parent passed in during construction 091 * - suggest child indent level 092 * - allows for some tokens to be on same line (ie inner classes OBJBLOCK) 093 * and not increase indentation level 094 * - looked at using double dispatch for suggestedChildLevel(), but it 095 * doesn't seem worthwhile, at least now 096 * - both tabs and spaces are considered whitespace in front of the line... 097 * tabs are converted to spaces 098 * - block parents with parens -- for, while, if, etc... -- are checked that 099 * they match the level of the parent 100 * </pre> 101 * 102 * @author jrichard 103 * @author o_sukhodolsky 104 * @author Maikel Steneker 105 * @author maxvetrenko 106 */ 107public class IndentationCheck extends Check 108{ 109 /** Default indentation amount - based on Sun */ 110 private static final int DEFAULT_INDENTATION = 4; 111 112 /** how many tabs or spaces to use */ 113 private int mBasicOffset = DEFAULT_INDENTATION; 114 115 /** how much to indent a case label */ 116 private int mCaseIndentationAmount = DEFAULT_INDENTATION; 117 118 /** how far brace should be indented when on next line */ 119 private int mBraceAdjustment; 120 121 /** how far throws should be indented when on next line */ 122 private int mThrowsIndentationAmount = DEFAULT_INDENTATION; 123 124 /** how much to indent an array initialization when on next line */ 125 private int mArrayInitIndentationAmount = DEFAULT_INDENTATION; 126 127 /** how far continuation line should be indented when line-wrapping is present */ 128 private int mLineWrappingIndentation = DEFAULT_INDENTATION; 129 130 /** handlers currently in use */ 131 private final FastStack<ExpressionHandler> mHandlers = 132 FastStack.newInstance(); 133 134 /** factory from which handlers are distributed */ 135 private final HandlerFactory mHandlerFactory = new HandlerFactory(); 136 137 /** Creates a new instance of IndentationCheck. */ 138 public IndentationCheck() 139 { 140 } 141 142 /** 143 * Set the basic offset. 144 * 145 * @param aBasicOffset the number of tabs or spaces to indent 146 */ 147 public void setBasicOffset(int aBasicOffset) 148 { 149 mBasicOffset = aBasicOffset; 150 } 151 152 /** 153 * Get the basic offset. 154 * 155 * @return the number of tabs or spaces to indent 156 */ 157 public int getBasicOffset() 158 { 159 return mBasicOffset; 160 } 161 162 /** 163 * Adjusts brace indentation (positive offset). 164 * 165 * @param aAdjustmentAmount the brace offset 166 */ 167 public void setBraceAdjustment(int aAdjustmentAmount) 168 { 169 mBraceAdjustment = aAdjustmentAmount; 170 } 171 172 /** 173 * Get the brace adjustment amount. 174 * 175 * @return the positive offset to adjust braces 176 */ 177 public int getBraceAdjustement() 178 { 179 return mBraceAdjustment; 180 } 181 182 /** 183 * Set the case indentation level. 184 * 185 * @param aAmount the case indentation level 186 */ 187 public void setCaseIndent(int aAmount) 188 { 189 mCaseIndentationAmount = aAmount; 190 } 191 192 /** 193 * Get the case indentation level. 194 * 195 * @return the case indentation level 196 */ 197 public int getCaseIndent() 198 { 199 return mCaseIndentationAmount; 200 } 201 202 /** 203 * Set the throws indentation level. 204 * 205 * @param aThrowsIndent the throws indentation level 206 */ 207 public void setThrowsIndent(int aThrowsIndent) 208 { 209 mThrowsIndentationAmount = aThrowsIndent; 210 } 211 212 /** 213 * Get the throws indentation level. 214 * 215 * @return the throws indentation level 216 */ 217 public int getThrowsIndent() 218 { 219 return this.mThrowsIndentationAmount; 220 } 221 222 /** 223 * Set the array initialisation indentation level. 224 * 225 * @param aArrayInitIndent the array initialisation indentation level 226 */ 227 public void setArrayInitIndent(int aArrayInitIndent) 228 { 229 mArrayInitIndentationAmount = aArrayInitIndent; 230 } 231 232 /** 233 * Get the line-wrapping indentation level. 234 * 235 * @return the initialisation indentation level 236 */ 237 public int getArrayInitIndent() 238 { 239 return this.mArrayInitIndentationAmount; 240 } 241 242 /** 243 * Get the array line-wrapping indentation level. 244 * 245 * @return the line-wrapping indentation level 246 */ 247 public int getLineWrappingIndentation() 248 { 249 return mLineWrappingIndentation; 250 } 251 252 253 /** 254 * Set the line-wrapping indentation level. 255 * 256 * @param aLineWrappingIndentation the line-wrapping indentation level 257 */ 258 public void setLineWrappingIndentation(int aLineWrappingIndentation) 259 { 260 mLineWrappingIndentation = aLineWrappingIndentation; 261 } 262 263 /** 264 * Log an error message. 265 * 266 * @param aLine the line number where the error was found 267 * @param aKey the message that describes the error 268 * @param aArgs the details of the message 269 * 270 * @see java.text.MessageFormat 271 */ 272 public void indentationLog(int aLine, String aKey, Object... aArgs) 273 { 274 super.log(aLine, aKey, aArgs); 275 } 276 277 /** 278 * Get the width of a tab. 279 * 280 * @return the width of a tab 281 */ 282 public int getIndentationTabWidth() 283 { 284 return getTabWidth(); 285 } 286 287 @Override 288 public int[] getDefaultTokens() 289 { 290 return mHandlerFactory.getHandledTypes(); 291 } 292 293 @Override 294 public void beginTree(DetailAST aAst) 295 { 296 mHandlerFactory.clearCreatedHandlers(); 297 mHandlers.clear(); 298 mHandlers.push(new PrimordialHandler(this)); 299 } 300 301 @Override 302 public void visitToken(DetailAST aAST) 303 { 304 final ExpressionHandler handler = mHandlerFactory.getHandler(this, aAST, 305 mHandlers.peek()); 306 mHandlers.push(handler); 307 try { 308 handler.checkIndentation(); 309 } 310 catch (final NullPointerException npe) { 311 npe.printStackTrace(); 312 } 313 } 314 315 @Override 316 public void leaveToken(DetailAST aAST) 317 { 318 mHandlers.pop(); 319 } 320 321 /** 322 * Accessor for the handler factory. 323 * 324 * @return the handler factory 325 */ 326 final HandlerFactory getHandlerFactory() 327 { 328 return mHandlerFactory; 329 } 330}