1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks.indentation;
21
22 import com.puppycrawl.tools.checkstyle.api.DetailAST;
23 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
24 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
25
26
27
28
29
30 public class MethodCallHandler extends AbstractExpressionHandler {
31
32
33
34
35 private final IndentationCheck indentCheck;
36
37
38
39
40
41
42
43
44
45 public MethodCallHandler(IndentationCheck indentCheck,
46 DetailAST ast, AbstractExpressionHandler parent) {
47 super(indentCheck, "method call", ast, parent);
48 this.indentCheck = indentCheck;
49 }
50
51 @Override
52 protected IndentLevel getIndentImpl() {
53 final IndentLevel indentLevel;
54
55
56 if (getParent() instanceof MethodCallHandler container) {
57 if (TokenUtil.areOnSameLine(container.getMainAst(), getMainAst())
58 || isChainedMethodCallWrapped()
59 || areMethodsChained(container.getMainAst(), getMainAst())) {
60 indentLevel = container.getIndent();
61 }
62
63
64 else {
65 indentLevel = new IndentLevel(container.getIndent(),
66 getIndentCheck().getLineWrappingIndentation());
67 }
68 }
69 else if (getMainAst().getFirstChild().getType() == TokenTypes.LITERAL_NEW) {
70 indentLevel = super.getIndentImpl();
71 }
72 else {
73
74
75 final DetailAstSet astSet = new DetailAstSet(indentCheck);
76 findSubtreeAst(astSet, getMainAst().getFirstChild(), true);
77 final int firstCol = expandedTabsColumnNo(astSet.firstLine());
78 final int lineStart = getLineStart(getFirstAst(getMainAst()));
79 if (lineStart == firstCol) {
80 indentLevel = super.getIndentImpl();
81 }
82 else {
83 indentLevel = new IndentLevel(lineStart);
84 }
85 }
86 return indentLevel;
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 private static boolean areMethodsChained(DetailAST ast1, DetailAST ast2) {
105 final DetailAST rparen = ast1.findFirstToken(TokenTypes.RPAREN);
106 return TokenUtil.areOnSameLine(rparen, ast2);
107 }
108
109
110
111
112
113
114 private boolean isChainedMethodCallWrapped() {
115 final DetailAST main = getMainAst();
116 final DetailAST dot = main.getFirstChild();
117 final DetailAST target = dot.getFirstChild();
118
119 final DetailAST dot1 = target.getFirstChild();
120 DetailAST target1 = dot1;
121 while (target1.getFirstChild() != null
122 && target1.getType() != TokenTypes.METHOD_CALL) {
123 target1 = target1.getFirstChild();
124 }
125
126 return dot1.getType() == TokenTypes.DOT
127 && target1.getType() == TokenTypes.METHOD_CALL;
128 }
129
130
131
132
133
134
135
136
137
138 private static DetailAST getFirstAst(DetailAST ast) {
139
140
141
142 DetailAST astNode = ast.getFirstChild();
143 while (astNode.getType() == TokenTypes.DOT) {
144 astNode = astNode.getFirstChild();
145 }
146 return astNode;
147 }
148
149
150
151
152
153
154
155 private DetailAST getMethodIdentAst() {
156 DetailAST ast = getMainAst();
157 if (ast.getType() != TokenTypes.SUPER_CTOR_CALL) {
158 ast = ast.getFirstChild();
159 if (ast.getType() == TokenTypes.DOT) {
160 ast = ast.getLastChild();
161 }
162 }
163 return ast;
164 }
165
166 @Override
167 public IndentLevel getSuggestedChildIndent(AbstractExpressionHandler child) {
168
169
170
171
172
173
174 final DetailAST ident = getMethodIdentAst();
175 final DetailAST rparen = getMainAst().findFirstToken(TokenTypes.RPAREN);
176 IndentLevel suggestedLevel = new IndentLevel(getLineStart(ident));
177 if (!TokenUtil.areOnSameLine(child.getMainAst().getFirstChild(), ident)) {
178 suggestedLevel = new IndentLevel(suggestedLevel,
179 getBasicOffset(),
180 getIndentCheck().getLineWrappingIndentation());
181 }
182
183
184
185 if (getLineStart(rparen) == rparen.getColumnNo()) {
186 suggestedLevel = IndentLevel.addAcceptable(suggestedLevel, new IndentLevel(
187 getParent().getSuggestedChildIndent(this),
188 getIndentCheck().getLineWrappingIndentation()
189 ));
190 }
191
192 return suggestedLevel;
193 }
194
195 @Override
196 public void checkIndentation() {
197 DetailAST lparen = null;
198 if (getMainAst().getType() == TokenTypes.METHOD_CALL) {
199 final DetailAST exprNode = getMainAst().getParent();
200 if (exprNode.getParent().getType() == TokenTypes.SLIST) {
201 checkExpressionSubtree(getMainAst().getFirstChild(), getIndent(), false, false);
202 lparen = getMainAst();
203 }
204 }
205 else {
206
207 lparen = getMainAst().getFirstChild();
208 }
209
210 if (lparen != null) {
211 final DetailAST rparen = getMainAst().findFirstToken(TokenTypes.RPAREN);
212 checkLeftParen(lparen);
213
214 if (!TokenUtil.areOnSameLine(rparen, lparen)) {
215 checkExpressionSubtree(
216 getMainAst().findFirstToken(TokenTypes.ELIST),
217 new IndentLevel(getIndent(), getBasicOffset()),
218 false, true);
219
220 checkRparenIndent(lparen, rparen);
221 checkWrappingIndentation(getMainAst(), getCallLastNode(getMainAst()));
222 }
223 }
224 }
225
226
227
228
229
230
231
232 private void checkRparenIndent(DetailAST lparen, DetailAST rparen) {
233 final int rparenLevel = expandedTabsColumnNo(rparen);
234 final int lparenLevel = expandedTabsColumnNo(lparen);
235
236 final IndentLevel standardIndent = getIndent();
237
238
239 IndentLevel enhancedIndent = standardIndent;
240 if (getParent() instanceof MethodCallHandler) {
241 final int lineStart = getLineStart(getFirstAst(getMainAst()));
242 if (lineStart != standardIndent.getFirstIndentLevel()) {
243 enhancedIndent = IndentLevel.addAcceptable(standardIndent,
244 new IndentLevel(lineStart));
245 }
246 }
247
248 if (rparenLevel != lparenLevel + 1
249 && !enhancedIndent.isAcceptable(rparenLevel)
250 && isOnStartOfLine(rparen)) {
251 logError(rparen, "rparen", rparenLevel);
252 }
253 }
254
255 @Override
256 protected boolean shouldIncreaseIndent() {
257 return false;
258 }
259
260
261
262
263
264
265
266
267
268 private static DetailAST getCallLastNode(DetailAST firstNode) {
269 return firstNode.getLastChild();
270 }
271
272 }