1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.util;
4 import com.intellij.psi.*;
5 import com.intellij.psi.util.MethodSignature;
6 import com.intellij.psi.util.MethodSignatureUtil;
7 import org.jetbrains.annotations.Contract;
8 import org.jetbrains.annotations.Nullable;
10 import java.util.HashSet;
13 public class JavaPsiConstructorUtil {
15 * Finds call to another constructor within this constructor (either chained or super)
16 * @param constructor constructor to search in
17 * @return found this/super constructor method call or null if not found or supplied method is null or not a constructor
19 @Contract("null -> null")
21 public static PsiMethodCallExpression findThisOrSuperCallInConstructor(@Nullable PsiMethod constructor) {
22 if (constructor == null || !constructor.isConstructor()) return null;
23 PsiCodeBlock body = constructor.getBody();
24 if (body == null) return null;
25 PsiElement bodyElement = body.getFirstBodyElement();
26 while (bodyElement != null && !(bodyElement instanceof PsiStatement)) {
27 bodyElement = bodyElement.getNextSibling();
29 if (!(bodyElement instanceof PsiExpressionStatement)) return null;
30 PsiMethodCallExpression call =
31 ObjectUtils.tryCast(((PsiExpressionStatement)bodyElement).getExpression(), PsiMethodCallExpression.class);
32 if (isConstructorCall(call)) return call;
37 * Returns true if given element is a chained constructor call
38 * @param call element to check
39 * @return true if given element is a chained constructor call
41 @Contract("null -> false")
42 public static boolean isChainedConstructorCall(@Nullable PsiElement call) {
43 if (!(call instanceof PsiMethodCallExpression)) return false;
44 PsiElement child = ((PsiMethodCallExpression)call).getMethodExpression().getReferenceNameElement();
45 return child instanceof PsiKeyword && child.textMatches(PsiKeyword.THIS);
49 * Returns true if given element is a super constructor call
50 * @param call element to check
51 * @return true if given element is a super constructor call
53 @Contract("null -> false")
54 public static boolean isSuperConstructorCall(@Nullable PsiElement call) {
55 if (!(call instanceof PsiMethodCallExpression)) return false;
56 PsiElement child = ((PsiMethodCallExpression)call).getMethodExpression().getReferenceNameElement();
57 return child instanceof PsiKeyword && child.textMatches(PsiKeyword.SUPER);
61 * Returns true if given element is chained or super constructor call
62 * @param call element to check
63 * @return true if given element is chained or super constructor call
65 @Contract("null -> false")
66 public static boolean isConstructorCall(@Nullable PsiElement call) {
67 if (!(call instanceof PsiMethodCallExpression)) return false;
68 PsiElement child = ((PsiMethodCallExpression)call).getMethodExpression().getReferenceNameElement();
69 return child instanceof PsiKeyword && (child.textMatches(PsiKeyword.SUPER) || child.textMatches(PsiKeyword.THIS));
72 public static PsiMethod findConstructorInSuper(PsiMethod constructor) {
73 return findConstructorInSuper(constructor, new HashSet<>());
76 private static PsiMethod findConstructorInSuper(PsiMethod constructor, Set<? super PsiMethod> visited) {
77 if (visited.contains(constructor)) return null;
78 visited.add(constructor);
79 PsiMethodCallExpression call = findThisOrSuperCallInConstructor(constructor);
80 if (isSuperConstructorCall(call)) {
81 PsiMethod superConstructor = call.resolveMethod();
82 if (superConstructor != null) {
83 return superConstructor;
85 } else if (isChainedConstructorCall(call)) {
86 PsiMethod chainedConstructor = call.resolveMethod();
87 if (chainedConstructor != null) {
88 return findConstructorInSuper(chainedConstructor, visited);
93 PsiClass containingClass = constructor.getContainingClass();
94 if (containingClass != null) {
95 PsiClass superClass = containingClass.getSuperClass();
96 if (superClass != null && superClass.getName() != null) {
97 MethodSignature defConstructor = MethodSignatureUtil.createMethodSignature(superClass.getName(), PsiType.EMPTY_ARRAY,
98 PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY, true);
99 return MethodSignatureUtil.findMethodBySignature(superClass, defConstructor, false);