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 && !isInvocationTarget(child)) {
179 suggestedLevel = new IndentLevel(suggestedLevel,
180 getBasicOffset(),
181 getIndentCheck().getLineWrappingIndentation());
182 }
183
184
185
186 if (getLineStart(rparen) == rparen.getColumnNo()) {
187 suggestedLevel = IndentLevel.addAcceptable(suggestedLevel, new IndentLevel(
188 getParent().getSuggestedChildIndent(this),
189 getIndentCheck().getLineWrappingIndentation()
190 ));
191 }
192
193 return suggestedLevel;
194 }
195
196
197
198
199
200
201
202
203
204 private boolean isInvocationTarget(AbstractExpressionHandler child) {
205 boolean crossedElist = false;
206 DetailAST node = child.getMainAst().getParent();
207 while (node != null && node != getMainAst()) {
208 if (node.getType() == TokenTypes.ELIST) {
209 crossedElist = true;
210 break;
211 }
212 node = node.getParent();
213 }
214 return !crossedElist;
215 }
216
217 @Override
218 public void checkIndentation() {
219 DetailAST lparen = null;
220 if (getMainAst().getType() == TokenTypes.METHOD_CALL) {
221 final DetailAST exprNode = getMainAst().getParent();
222 if (exprNode.getParent().getType() == TokenTypes.SLIST) {
223 checkExpressionSubtree(getMainAst().getFirstChild(), getIndent(), false, false);
224 lparen = getMainAst();
225 }
226 }
227 else {
228
229 lparen = getMainAst().getFirstChild();
230 }
231
232 if (lparen != null) {
233 final DetailAST rparen = getMainAst().findFirstToken(TokenTypes.RPAREN);
234 checkLeftParen(lparen);
235
236 if (!TokenUtil.areOnSameLine(rparen, lparen)) {
237 checkExpressionSubtree(
238 getMainAst().findFirstToken(TokenTypes.ELIST),
239 new IndentLevel(getIndent(), getBasicOffset()),
240 false, true);
241
242 checkRparenIndent(lparen, rparen);
243 checkWrappingIndentation(getMainAst(), getCallLastNode(getMainAst()));
244 }
245 }
246 }
247
248
249
250
251
252
253
254 private void checkRparenIndent(DetailAST lparen, DetailAST rparen) {
255 final int rparenLevel = expandedTabsColumnNo(rparen);
256 final int lparenLevel = expandedTabsColumnNo(lparen);
257
258 final IndentLevel standardIndent = getIndent();
259
260
261 IndentLevel enhancedIndent = standardIndent;
262 if (getParent() instanceof MethodCallHandler) {
263 final int lineStart = getLineStart(getFirstAst(getMainAst()));
264 if (lineStart != standardIndent.getFirstIndentLevel()) {
265 enhancedIndent = IndentLevel.addAcceptable(standardIndent,
266 new IndentLevel(lineStart));
267 }
268 }
269
270 if (rparenLevel != lparenLevel + 1
271 && !enhancedIndent.isAcceptable(rparenLevel)
272 && isOnStartOfLine(rparen)) {
273 logError(rparen, "rparen", rparenLevel);
274 }
275 }
276
277 @Override
278 protected boolean shouldIncreaseIndent() {
279 return false;
280 }
281
282
283
284
285
286
287
288
289
290 private static DetailAST getCallLastNode(DetailAST firstNode) {
291 return firstNode.getLastChild();
292 }
293
294 }