1 ////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code for adherence to a set of rules.
3 // Copyright (C) 2001-2015 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ////////////////////////////////////////////////////////////////////////////////
19 package com.puppycrawl.tools.checkstyle.checks.indentation;
20
21 import com.puppycrawl.tools.checkstyle.api.DetailAST;
22 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
23
24 /**
25 * Handler for method calls.
26 *
27 * @author jrichard
28 */
29 public class MethodCallHandler extends ExpressionHandler
30 {
31 /**
32 * Construct an instance of this handler with the given indentation check,
33 * abstract syntax tree, and parent handler.
34 *
35 * @param indentCheck the indentation check
36 * @param ast the abstract syntax tree
37 * @param parent the parent handler
38 */
39 public MethodCallHandler(IndentationCheck indentCheck,
40 DetailAST ast, ExpressionHandler parent)
41 {
42 super(indentCheck,
43 ast.getType() == TokenTypes.METHOD_CALL
44 ? "method call" : "ctor call",
45 ast,
46 parent);
47 }
48
49 @Override
50 protected IndentLevel getLevelImpl()
51 {
52 // if inside a method call's params, this could be part of
53 // an expression, so get the previous line's start
54 if (getParent() instanceof MethodCallHandler) {
55 final MethodCallHandler container =
56 (MethodCallHandler) getParent();
57 if (container != null) {
58 if (areOnSameLine(container.getMainAst(), getMainAst())) {
59 return container.getLevel();
60 }
61
62 // we should increase indentation only if this is the first
63 // chained method call which was moved to the next line
64 final DetailAST main = getMainAst();
65 final DetailAST dot = main.getFirstChild();
66 final DetailAST target = dot.getFirstChild();
67
68 if (dot.getType() == TokenTypes.DOT
69 && target.getType() == TokenTypes.METHOD_CALL)
70 {
71 final DetailAST dot1 = target.getFirstChild();
72 final DetailAST target1 = dot1.getFirstChild();
73
74 if (dot1.getType() == TokenTypes.DOT
75 && target1.getType() == TokenTypes.METHOD_CALL)
76 {
77 return container.getLevel();
78 }
79 }
80 return new IndentLevel(container.getLevel(), getBasicOffset());
81 }
82
83 // if we get here, we are the child of the left hand side (name
84 // side) of a method call with no "containing" call, use
85 // the first non-method call parent
86
87 ExpressionHandler p = getParent();
88 while (p instanceof MethodCallHandler) {
89 p = p.getParent();
90 }
91 return p.suggestedChildLevel(this);
92 }
93
94 // if our expression isn't first on the line, just use the start
95 // of the line
96 final LineSet lines = new LineSet();
97 findSubtreeLines(lines, getMainAst().getFirstChild(), true);
98 final int firstCol = lines.firstLineCol();
99 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 getMethodCallLastNode(getMainAst()));
179 lineWrap.checkIndentation();
180 }
181
182 @Override
183 protected boolean shouldIncreaseIndent()
184 {
185 return false;
186 }
187
188 /**
189 * Returns method call right paren.
190 * @param firstNode
191 * method call ast(TokenTypes.METHOD_CALL)
192 * @return ast node containing right paren for specified method call. If
193 * method calls are chained returns right paren for last call.
194 */
195 private static DetailAST getMethodCallLastNode(DetailAST firstNode)
196 {
197 DetailAST lastNode;
198 if (firstNode.getNextSibling() == null) {
199 lastNode = firstNode.getLastChild();
200 }
201 else {
202 lastNode = firstNode.getNextSibling();
203 }
204 return lastNode;
205 }
206 }