Cleanup: NotNull/Nullable
[idea/community.git] / java / java-psi-api / src / com / intellij / util / JavaPsiConstructorUtil.java
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;
3
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;
9
10 import java.util.HashSet;
11 import java.util.Set;
12
13 public class JavaPsiConstructorUtil {
14   /**
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
18    */
19   @Contract("null -> null")
20   @Nullable
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();
28     }
29     if (!(bodyElement instanceof PsiExpressionStatement)) return null;
30     PsiMethodCallExpression call =
31       ObjectUtils.tryCast(((PsiExpressionStatement)bodyElement).getExpression(), PsiMethodCallExpression.class);
32     if (isConstructorCall(call)) return call;
33     return null;
34   }
35
36   /**
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
40    */
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);
46   }
47
48   /**
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
52    */
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);
58   }
59
60   /**
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
64    */
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));
70   }
71
72   public static PsiMethod findConstructorInSuper(PsiMethod constructor) {
73     return findConstructorInSuper(constructor, new HashSet<>());
74   }
75
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;
84       }
85     } else if (isChainedConstructorCall(call)) {
86       PsiMethod chainedConstructor = call.resolveMethod();
87       if (chainedConstructor != null) {
88         return findConstructorInSuper(chainedConstructor, visited);
89       }
90       return null;
91     }
92
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);
100       }
101     }
102     return null;
103   }
104 }