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.DetailAST; 022import com.puppycrawl.tools.checkstyle.api.TokenTypes; 023 024/** 025 * Handler for method calls. 026 * 027 * @author jrichard 028 */ 029public class MethodCallHandler extends ExpressionHandler 030{ 031 /** 032 * Construct an instance of this handler with the given indentation check, 033 * abstract syntax tree, and parent handler. 034 * 035 * @param indentCheck the indentation check 036 * @param ast the abstract syntax tree 037 * @param parent the parent handler 038 */ 039 public MethodCallHandler(IndentationCheck indentCheck, 040 DetailAST ast, ExpressionHandler parent) 041 { 042 super(indentCheck, 043 ast.getType() == TokenTypes.METHOD_CALL 044 ? "method call" : "ctor call", 045 ast, 046 parent); 047 } 048 049 @Override 050 protected IndentLevel getLevelImpl() 051 { 052 // if inside a method call's params, this could be part of 053 // an expression, so get the previous line's start 054 if (getParent() instanceof MethodCallHandler) { 055 final MethodCallHandler container = 056 ((MethodCallHandler) getParent()); 057 if (container != null) { 058 if (areOnSameLine(container.getMainAst(), getMainAst())) { 059 return container.getLevel(); 060 } 061 062 // we should increase indentation only if this is the first 063 // chained method call which was moved to the next line 064 final DetailAST main = getMainAst(); 065 final DetailAST dot = main.getFirstChild(); 066 final DetailAST target = dot.getFirstChild(); 067 068 if ((dot.getType() == TokenTypes.DOT) 069 && (target.getType() == TokenTypes.METHOD_CALL)) 070 { 071 final DetailAST dot1 = target.getFirstChild(); 072 final DetailAST target1 = dot1.getFirstChild(); 073 074 if ((dot1.getType() == TokenTypes.DOT) 075 && (target1.getType() == TokenTypes.METHOD_CALL)) 076 { 077 return container.getLevel(); 078 } 079 } 080 return new IndentLevel(container.getLevel(), getBasicOffset()); 081 } 082 083 // if we get here, we are the child of the left hand side (name 084 // side) of a method call with no "containing" call, use 085 // the first non-method call parent 086 087 ExpressionHandler p = getParent(); 088 while (p instanceof MethodCallHandler) { 089 p = p.getParent(); 090 } 091 return p.suggestedChildLevel(this); 092 } 093 094 // if our expression isn't first on the line, just use the start 095 // of the line 096 final LineSet lines = new LineSet(); 097 findSubtreeLines(lines, getMainAst().getFirstChild(), true); 098 final int firstCol = lines.firstLineCol(); 099 final int lineStart = getLineStart(getFirstAst(getMainAst())); 100 if (lineStart != firstCol) { 101 return new IndentLevel(lineStart); 102 } 103 return super.getLevelImpl(); 104 } 105 106 /** 107 * Get the first AST of the specified method call. 108 * 109 * @param ast 110 * the method call 111 * 112 * @return the first AST of the specified method call 113 */ 114 private DetailAST getFirstAst(DetailAST ast) 115 { 116 // walk down the first child part of the dots that make up a method 117 // call name 118 119 DetailAST astNode = ast.getFirstChild(); 120 while ((astNode != null) && (astNode.getType() == TokenTypes.DOT)) { 121 astNode = astNode.getFirstChild(); 122 } 123 124 if (astNode == null) { 125 astNode = ast; 126 } 127 128 return astNode; 129 } 130 131 @Override 132 public IndentLevel suggestedChildLevel(ExpressionHandler child) 133 { 134 // for whatever reason a method that crosses lines, like asList 135 // here: 136 // System.out.println("methods are: " + Arrays.asList( 137 // new String[] {"method"}).toString()); 138 // will not have the right line num, so just get the child name 139 140 final DetailAST first = getMainAst().getFirstChild(); 141 int indentLevel = getLineStart(first); 142 if (!areOnSameLine(child.getMainAst().getFirstChild(), 143 getMainAst().getFirstChild())) 144 { 145 indentLevel += getBasicOffset(); 146 } 147 return new IndentLevel(indentLevel); 148 } 149 150 @Override 151 public void checkIndentation() 152 { 153 final DetailAST exprNode = getMainAst().getParent(); 154 if (exprNode.getParent().getType() != TokenTypes.LCURLY 155 && exprNode.getParent().getType() != TokenTypes.SLIST) 156 { 157 return; 158 } 159 final DetailAST methodName = getMainAst().getFirstChild(); 160 checkExpressionSubtree(methodName, getLevel(), false, false); 161 162 final DetailAST lparen = getMainAst(); 163 final DetailAST rparen = getMainAst().findFirstToken(TokenTypes.RPAREN); 164 checkLParen(lparen); 165 166 if (rparen.getLineNo() == lparen.getLineNo()) { 167 return; 168 } 169 170 checkExpressionSubtree( 171 getMainAst().findFirstToken(TokenTypes.ELIST), 172 new IndentLevel(getLevel(), getBasicOffset()), 173 false, true); 174 175 checkRParen(lparen, rparen); 176 final LineWrappingHandler lineWrap = 177 new LineWrappingHandler(getIndentCheck(), getMainAst()) { 178 @Override 179 public DetailAST findLastNode(DetailAST firstNode) 180 { 181 DetailAST lastNode; 182 if (getFirstNode().getNextSibling() == null) { 183 lastNode = getFirstNode().getLastChild(); 184 } 185 else { 186 lastNode = getFirstNode().getNextSibling(); 187 } 188 return lastNode; 189 } 190 191 @Override 192 public int getCurrentIndentation() 193 { 194 DetailAST curNode = getFirstNode(); 195 while (curNode.getType() != TokenTypes.IDENT) { 196 curNode = curNode.getFirstChild(); 197 } 198 return curNode.getColumnNo() + getIndentLevel(); 199 } 200 }; 201 lineWrap.checkIndentation(); 202 } 203 204 @Override 205 protected boolean shouldIncreaseIndent() 206 { 207 return false; 208 } 209}