[duplicates] enable duplicates analysis in PyCharm/WebStorm/PhpStorm/RubyMine
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / extractMethodObject / reflect / ConstructorReflectionAccessor.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.diagnostic.Logger;
5 import com.intellij.openapi.util.text.StringUtil;
6 import com.intellij.psi.*;
7 import com.intellij.psi.util.ClassUtil;
8 import com.intellij.refactoring.extractMethodObject.ItemToReplaceDescriptor;
9 import one.util.streamex.StreamEx;
10 import org.jetbrains.annotations.NotNull;
11 import org.jetbrains.annotations.Nullable;
12
13 import java.util.ArrayList;
14 import java.util.List;
15
16 /**
17  * @author Vitaliy.Bibaev
18  */
19 public class ConstructorReflectionAccessor extends ReflectionAccessorBase<ConstructorReflectionAccessor.ConstructorDescriptor> {
20   private static final Logger LOG = Logger.getInstance(ConstructorReflectionAccessor.class);
21
22   protected ConstructorReflectionAccessor(@NotNull PsiClass psiClass,
23                                           @NotNull PsiElementFactory elementFactory) {
24     super(psiClass, elementFactory);
25   }
26
27   @Override
28   protected List<ConstructorDescriptor> findItemsToReplace(@NotNull PsiElement element) {
29     List<ConstructorDescriptor> result = new ArrayList<>();
30
31     element.accept(new JavaRecursiveElementVisitor() {
32       @Override
33       public void visitNewExpression(PsiNewExpression expression) {
34         super.visitNewExpression(expression);
35         if (expression.getAnonymousClass() != null || expression.getArrayInitializer() != null) return;
36         ConstructorDescriptor descriptor = ConstructorDescriptor.createIfInaccessible(expression);
37         if (descriptor != null) {
38           result.add(descriptor);
39         }
40       }
41     });
42
43     return result;
44   }
45
46   @Override
47   protected void grantAccess(@NotNull ConstructorDescriptor descriptor) {
48     String className = ClassUtil.getJVMClassName(descriptor.psiClass);
49     String returnType = PsiReflectionAccessUtil.getAccessibleReturnType(descriptor.newExpression, descriptor.psiClass);
50     PsiExpressionList argumentList = descriptor.newExpression.getArgumentList();
51     if (className == null || argumentList == null || returnType == null) {
52       LOG.warn("code is incomplete: " + descriptor.newExpression);
53       return;
54     }
55
56     String methodName = PsiReflectionAccessUtil.getUniqueMethodName(getOuterClass(), methodName(className));
57     ReflectionAccessMethodBuilder methodBuilder = new ReflectionAccessMethodBuilder(methodName);
58     methodBuilder.accessedConstructor(className)
59                  .setStatic(getOuterClass().hasModifierProperty(PsiModifier.STATIC))
60                  .setReturnType(returnType);
61     if (descriptor.constructor != null) {
62       methodBuilder.addParameters(descriptor.constructor.getParameterList());
63     }
64
65     PsiMethod newPsiMethod = methodBuilder.build(getElementFactory(), getOuterClass());
66     getOuterClass().add(newPsiMethod);
67     String args = StreamEx.of(argumentList.getExpressions()).map(x -> x.getText()).joining(", ", "(", ")");
68     String newCallExpression = newPsiMethod.getName() + args;
69     descriptor.newExpression.replace(getElementFactory().createExpressionFromText(newCallExpression, descriptor.newExpression));
70   }
71
72   @NotNull
73   private static String methodName(@NotNull String jvmClassName) {
74     String name = StringUtil.getShortName(jvmClassName);
75     return "new" + StringUtil.capitalize(name);
76   }
77
78   public static class ConstructorDescriptor implements ItemToReplaceDescriptor {
79     public final PsiNewExpression newExpression;
80     public final PsiClass psiClass;
81
82     // null if and only if default constructor is used
83     @Nullable public final PsiMethod constructor;
84
85     @Nullable
86     public static ConstructorDescriptor createIfInaccessible(@NotNull PsiNewExpression expression) {
87       PsiMethod constructor = expression.resolveConstructor();
88       if (constructor != null) {
89         PsiClass containingClass = constructor.getContainingClass();
90         if (containingClass != null && !PsiReflectionAccessUtil.isAccessibleMember(constructor)) {
91           return new ConstructorDescriptor(expression, constructor, containingClass);
92         }
93       }
94       else {
95         PsiJavaCodeReferenceElement classReference = expression.getClassReference();
96         PsiElement referent = classReference != null ? classReference.resolve() : null;
97         if (referent instanceof PsiClass && !PsiReflectionAccessUtil.isAccessible((PsiClass)referent)) {
98           return new ConstructorDescriptor(expression, null, (PsiClass)referent);
99         }
100       }
101
102       return null;
103     }
104
105     public ConstructorDescriptor(@NotNull PsiNewExpression expression, @Nullable PsiMethod constructor, @NotNull PsiClass psiClass) {
106       newExpression = expression;
107       this.constructor = constructor;
108       this.psiClass = psiClass;
109     }
110   }
111 }