[duplicates] enable duplicates analysis in PyCharm/WebStorm/PhpStorm/RubyMine
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / extractMethodObject / reflect / PsiReflectionAccessUtil.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.refactoring.extractMethodObject.reflect;
3
4 import com.intellij.openapi.util.text.StringUtil;
5 import com.intellij.psi.*;
6 import com.intellij.psi.util.PsiTypesUtil;
7 import com.intellij.psi.util.PsiUtil;
8 import com.intellij.psi.util.TypeConversionUtil;
9 import com.intellij.util.ArrayUtil;
10 import com.siyeh.ig.psiutils.ExpectedTypeUtils;
11 import org.jetbrains.annotations.Contract;
12 import org.jetbrains.annotations.NotNull;
13 import org.jetbrains.annotations.Nullable;
14
15 import java.util.*;
16
17 /**
18  * @author Vitaliy.Bibaev
19  */
20 class PsiReflectionAccessUtil {
21   public static boolean isAccessibleMember(@NotNull PsiMember classMember) {
22     return classMember.hasModifierProperty(PsiModifier.PUBLIC) && isAccessible(classMember.getContainingClass());
23   }
24
25   /**
26    * Since we use new classloader for each "Evaluate expression" with compilation, the generated code has no
27    * access to all members excluding public
28    */
29   @Contract("null -> false")
30   public static boolean isAccessible(@Nullable PsiClass psiClass) {
31     if (psiClass == null) return false;
32
33     // currently, we use dummy psi class "_Array_" to represent arrays which is an inner of package-private _Dummy_ class.
34     if (PsiUtil.isArrayClass(psiClass)) return true;
35     while (psiClass != null) {
36       if (!psiClass.hasModifierProperty(PsiModifier.PUBLIC)) {
37         return false;
38       }
39
40       psiClass = psiClass.getContainingClass();
41     }
42
43     return true;
44   }
45
46   @Nullable
47   public static String extractQualifier(@NotNull PsiReferenceExpression referenceExpression) {
48     PsiExpression qualifierExpression = referenceExpression.getQualifierExpression();
49     PsiType expressionType = qualifierExpression != null ? qualifierExpression.getType() : null;
50     return expressionType == null ? null : qualifierExpression.getText();
51   }
52
53   @Contract(value = "null -> true")
54   public static boolean isQualifierAccessible(@Nullable PsiExpression qualifierExpression) {
55     if (qualifierExpression == null) return true;
56     PsiType type = qualifierExpression.getType();
57     PsiClass psiClass = PsiUtil.resolveClassInType(type);
58     return psiClass == null || isAccessible(psiClass);
59   }
60
61   @Nullable
62   public static String getAccessibleReturnType(@NotNull PsiExpression expression, @Nullable PsiType type) {
63     String expectedType = tryGetWeakestAccessibleExpectedType(expression);
64     if (expectedType != null) return expectedType;
65
66     PsiType nearestAccessibleBaseType = nearestAccessedType(type);
67     if (nearestAccessibleBaseType != null) return nearestAccessibleBaseType.getCanonicalText();
68
69     return nearestAccessibleBaseClass(PsiTypesUtil.getPsiClass(type));
70   }
71
72   @Nullable
73   public static String getAccessibleReturnType(@NotNull PsiExpression expression, @Nullable PsiClass psiClass) {
74     String expectedType = tryGetWeakestAccessibleExpectedType(expression);
75     if (expectedType != null) return expectedType;
76
77     return nearestAccessibleBaseClass(psiClass);
78   }
79
80   @Nullable
81   private static String tryGetWeakestAccessibleExpectedType(@NotNull PsiExpression expression) {
82     PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, true);
83     PsiType realType = expression.getType();
84     if (expectedType != null && realType != null) {
85       for (PsiType type: getAllAssignableSupertypes(realType, expectedType)) {
86         if (isAccessible(type)) {
87           return type.getCanonicalText();
88         }
89       }
90     }
91
92     return null;
93   }
94
95   @NotNull
96   private static List<PsiType> getAllAssignableSupertypes(@NotNull PsiType from, @NotNull PsiType to) {
97     Set<PsiType> types = new LinkedHashSet<>();
98     Queue<PsiType> queue = new LinkedList<>();
99     queue.offer(from);
100     while (!queue.isEmpty()) {
101       PsiType type = queue.poll();
102       if (to.isAssignableFrom(type)) {
103         types.add(type);
104         Arrays.stream(type.getSuperTypes()).forEach(queue::offer);
105       }
106     }
107
108     List<PsiType> result = new ArrayList<>(types);
109     Collections.reverse(result);
110     return result;
111   }
112
113   @NotNull
114   @Contract(pure = true)
115   public static String classForName(@NotNull String typeName) {
116     return TypeConversionUtil.isPrimitive(typeName) ? typeName + ".class" : "java.lang.Class.forName(\"" + typeName + "\")";
117   }
118
119   @NotNull
120   public static String getUniqueMethodName(@NotNull PsiClass psiClass, @NotNull String prefix) {
121     if (!StringUtil.isJavaIdentifier(prefix)) throw new IllegalArgumentException("prefix must be a correct java identifier: " + prefix);
122     int i = 1;
123     String name;
124     do {
125       name = prefix + i;
126       i++;
127     }
128     while (psiClass.findMethodsByName(name, false).length != 0);
129
130     return name;
131   }
132
133   private static boolean isAccessible(@NotNull PsiType type) {
134     if (type instanceof PsiArrayType) {
135       return isAccessible(type.getDeepComponentType());
136     }
137
138     return TypeConversionUtil.isPrimitiveAndNotNull(type) || isAccessible(PsiTypesUtil.getPsiClass(type));
139   }
140
141   @Nullable
142   private static PsiType nearestAccessedType(@Nullable PsiType type) {
143     while (type != null && !isAccessible(type)) {
144       type = ArrayUtil.getFirstElement(type.getSuperTypes());
145     }
146
147     return type;
148   }
149
150   @Contract("null -> null")
151   @Nullable
152   private static String nearestAccessibleBaseClass(@Nullable PsiClass psiClass) {
153     while (psiClass != null && !psiClass.hasModifierProperty(PsiModifier.PUBLIC)) {
154       psiClass = psiClass.getSuperClass();
155     }
156
157     return psiClass == null ? null : psiClass.getQualifiedName();
158   }
159 }