| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.refactoring.inheritanceToDelegation; |
| |
| import com.intellij.codeInsight.NullableNotNullManager; |
| import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil; |
| import com.intellij.codeInsight.generation.GenerateMembersUtil; |
| import com.intellij.codeInsight.generation.OverrideImplementUtil; |
| import com.intellij.find.findUsages.PsiElement2UsageTargetAdapter; |
| import com.intellij.lang.findUsages.DescriptiveNameUtil; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.openapi.wm.WindowManager; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.CodeStyleManager; |
| import com.intellij.psi.codeStyle.JavaCodeStyleManager; |
| import com.intellij.psi.codeStyle.VariableKind; |
| import com.intellij.psi.javadoc.PsiDocComment; |
| import com.intellij.psi.search.searches.ClassInheritorsSearch; |
| import com.intellij.psi.util.*; |
| import com.intellij.refactoring.BaseRefactoringProcessor; |
| import com.intellij.refactoring.RefactoringBundle; |
| import com.intellij.refactoring.inheritanceToDelegation.usageInfo.*; |
| import com.intellij.refactoring.ui.ConflictsDialog; |
| import com.intellij.refactoring.util.CommonRefactoringUtil; |
| import com.intellij.refactoring.util.ConflictsUtil; |
| import com.intellij.refactoring.util.RefactoringUIUtil; |
| import com.intellij.refactoring.util.classMembers.ClassMemberReferencesVisitor; |
| import com.intellij.refactoring.util.classRefs.ClassInstanceScanner; |
| import com.intellij.refactoring.util.classRefs.ClassReferenceScanner; |
| import com.intellij.refactoring.util.classRefs.ClassReferenceSearchingScanner; |
| import com.intellij.usageView.UsageInfo; |
| import com.intellij.usageView.UsageViewDescriptor; |
| import com.intellij.usages.UsageInfoToUsageConverter; |
| import com.intellij.usages.UsageTarget; |
| import com.intellij.usages.UsageViewManager; |
| import com.intellij.usages.UsageViewPresentation; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.VisibilityUtil; |
| import com.intellij.util.containers.HashMap; |
| import com.intellij.util.containers.MultiMap; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| |
| /** |
| * @author dsl |
| */ |
| public class InheritanceToDelegationProcessor extends BaseRefactoringProcessor { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.inheritanceToDelegation.InheritanceToDelegationProcessor"); |
| private final PsiClass myClass; |
| private final String myInnerClassName; |
| private final boolean myIsDelegateOtherMembers; |
| private final Set<PsiClass> myDelegatedInterfaces; |
| private final Set<PsiMethod> myDelegatedMethods; |
| private final HashMap<PsiMethod,String> myDelegatedMethodsVisibility; |
| private final Set<PsiMethod> myOverriddenMethods; |
| |
| private final PsiClass myBaseClass; |
| private final Set<PsiMember> myBaseClassMembers; |
| private final String myFieldName; |
| private final String myGetterName; |
| private final boolean myGenerateGetter; |
| private final Set<PsiClass> myBaseClassBases; |
| private Set<PsiClass> myClassImplementedInterfaces; |
| private final PsiElementFactory myFactory; |
| private final PsiClassType myBaseClassType; |
| private final PsiManager myManager; |
| private final boolean myIsInnerClassNeeded; |
| private Set<PsiClass> myClassInheritors; |
| private HashSet<PsiMethod> myAbstractDelegatedMethods; |
| private final Map<PsiClass, PsiSubstitutor> mySuperClassesToSubstitutors = new HashMap<PsiClass, PsiSubstitutor>(); |
| |
| |
| public InheritanceToDelegationProcessor(Project project, |
| PsiClass aClass, |
| @NotNull PsiClass targetBaseClass, |
| String fieldName, |
| String innerClassName, |
| PsiClass[] delegatedInterfaces, |
| PsiMethod[] delegatedMethods, |
| boolean delegateOtherMembers, |
| boolean generateGetter) { |
| super(project); |
| |
| myClass = aClass; |
| myInnerClassName = innerClassName; |
| myIsDelegateOtherMembers = delegateOtherMembers; |
| myManager = myClass.getManager(); |
| myFactory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory(); |
| |
| myBaseClass = targetBaseClass; |
| LOG.assertTrue( |
| // && !myBaseClass.isInterface() |
| myBaseClass.getQualifiedName() == null || !myBaseClass.getQualifiedName().equals(CommonClassNames.JAVA_LANG_OBJECT), myBaseClass); |
| myBaseClassMembers = getAllBaseClassMembers(); |
| myBaseClassBases = getAllBases(); |
| myBaseClassType = myFactory.createType(myBaseClass, getSuperSubstitutor (myBaseClass)); |
| |
| myIsInnerClassNeeded = InheritanceToDelegationUtil.isInnerClassNeeded(myClass, myBaseClass); |
| |
| |
| myFieldName = fieldName; |
| final String propertyName = JavaCodeStyleManager.getInstance(myProject).variableNameToPropertyName(myFieldName, VariableKind.FIELD); |
| myGetterName = PropertyUtil.suggestGetterName(propertyName, myBaseClassType); |
| myGenerateGetter = generateGetter; |
| |
| myDelegatedInterfaces = new LinkedHashSet<PsiClass>(); |
| addAll(myDelegatedInterfaces, delegatedInterfaces); |
| myDelegatedMethods = new LinkedHashSet<PsiMethod>(); |
| addAll(myDelegatedMethods, delegatedMethods); |
| myDelegatedMethodsVisibility = new HashMap<PsiMethod, String>(); |
| for (PsiMethod method : myDelegatedMethods) { |
| MethodSignature signature = method.getSignature(getSuperSubstitutor(method.getContainingClass())); |
| PsiMethod overridingMethod = MethodSignatureUtil.findMethodBySignature(myClass, signature, false); |
| if (overridingMethod != null) { |
| myDelegatedMethodsVisibility.put(method, |
| VisibilityUtil.getVisibilityModifier(overridingMethod.getModifierList())); |
| } |
| } |
| |
| myOverriddenMethods = getOverriddenMethods(); |
| } |
| |
| private PsiSubstitutor getSuperSubstitutor(final PsiClass superClass) { |
| PsiSubstitutor result = mySuperClassesToSubstitutors.get(superClass); |
| if (result == null) { |
| result = TypeConversionUtil.getSuperClassSubstitutor(superClass, myClass, PsiSubstitutor.EMPTY); |
| mySuperClassesToSubstitutors.put(superClass, result); |
| } |
| return result; |
| } |
| |
| @NotNull |
| protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) { |
| return new InheritanceToDelegationViewDescriptor(myClass); |
| } |
| |
| @NotNull |
| protected UsageInfo[] findUsages() { |
| ArrayList<UsageInfo> usages = new ArrayList<UsageInfo>(); |
| final PsiClass[] inheritors = ClassInheritorsSearch.search(myClass, true).toArray(PsiClass.EMPTY_ARRAY); |
| myClassInheritors = new HashSet<PsiClass>(); |
| myClassInheritors.add(myClass); |
| addAll(myClassInheritors, inheritors); |
| |
| { |
| ClassReferenceScanner scanner = new ClassReferenceSearchingScanner(myClass); |
| final MyClassInstanceReferenceVisitor instanceReferenceVisitor = new MyClassInstanceReferenceVisitor(myClass, usages); |
| scanner.processReferences(new ClassInstanceScanner(myClass, instanceReferenceVisitor)); |
| |
| MyClassMemberReferencesVisitor visitor = new MyClassMemberReferencesVisitor(usages, instanceReferenceVisitor); |
| myClass.accept(visitor); |
| |
| myClassImplementedInterfaces = instanceReferenceVisitor.getImplementedInterfaces(); |
| } |
| for (PsiClass inheritor : inheritors) { |
| processClass(inheritor, usages); |
| } |
| |
| return usages.toArray(new UsageInfo[usages.size()]); |
| } |
| |
| private FieldAccessibility getFieldAccessibility(PsiElement element) { |
| for (PsiClass aClass : myClassInheritors) { |
| if (PsiTreeUtil.isAncestor(aClass, element, false)) { |
| return new FieldAccessibility(true, aClass); |
| } |
| } |
| return FieldAccessibility.INVISIBLE; |
| } |
| |
| protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) { |
| final UsageInfo[] usagesIn = refUsages.get(); |
| ArrayList<UsageInfo> oldUsages = new ArrayList<UsageInfo>(); |
| addAll(oldUsages, usagesIn); |
| final ObjectUpcastedUsageInfo[] objectUpcastedUsageInfos = objectUpcastedUsages(usagesIn); |
| if (myPrepareSuccessfulSwingThreadCallback != null) { |
| MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>(); |
| if (objectUpcastedUsageInfos.length > 0) { |
| final String message = RefactoringBundle.message("instances.of.0.upcasted.to.1.were.found", |
| RefactoringUIUtil.getDescription(myClass, true), CommonRefactoringUtil.htmlEmphasize( |
| CommonClassNames.JAVA_LANG_OBJECT)); |
| |
| conflicts.putValue(myClass, message); |
| } |
| |
| analyzeConflicts(usagesIn, conflicts); |
| if (!conflicts.isEmpty()) { |
| ConflictsDialog conflictsDialog = prepareConflictsDialog(conflicts, usagesIn); |
| conflictsDialog.show(); |
| if (!conflictsDialog.isOK()){ |
| if (conflictsDialog.isShowConflicts()) prepareSuccessful(); |
| return false; |
| } |
| } |
| |
| if (objectUpcastedUsageInfos.length > 0) { |
| showObjectUpcastedUsageView(objectUpcastedUsageInfos); |
| setPreviewUsages(true); |
| } |
| } |
| ArrayList<UsageInfo> filteredUsages = filterUsages(oldUsages); |
| refUsages.set(filteredUsages.toArray(new UsageInfo[filteredUsages.size()])); |
| prepareSuccessful(); |
| return true; |
| } |
| |
| private void analyzeConflicts(UsageInfo[] usage, MultiMap<PsiElement, String> conflicts) { |
| HashMap<PsiElement,HashSet<PsiElement>> reportedNonDelegatedUsages = new HashMap<PsiElement, HashSet<PsiElement>>(); |
| HashMap<PsiClass,HashSet<PsiElement>> reportedUpcasts = new HashMap<PsiClass, HashSet<PsiElement>>(); |
| // HashSet reportedObjectUpcasts = new HashSet(); |
| |
| // final String nameJavaLangObject = ConflictsUtil.htmlEmphasize("java.lang.Object"); |
| final String classDescription = RefactoringUIUtil.getDescription(myClass, false); |
| |
| for (UsageInfo aUsage : usage) { |
| final PsiElement element = aUsage.getElement(); |
| if (aUsage instanceof InheritanceToDelegationUsageInfo) { |
| InheritanceToDelegationUsageInfo usageInfo = (InheritanceToDelegationUsageInfo)aUsage; |
| /*if (usageInfo instanceof ObjectUpcastedUsageInfo) { |
| PsiElement container = ConflictsUtil.getContainer(usageInfo.element); |
| if (!reportedObjectUpcasts.contains(container)) { |
| String message = "An instance of " + classDescription + " is upcasted to " |
| + nameJavaLangObject + " in " + ConflictsUtil.getDescription(container, true) + "."; |
| conflicts.add(message); |
| reportedObjectUpcasts.add(container); |
| } |
| } else*/ |
| if (!myIsDelegateOtherMembers && !usageInfo.getDelegateFieldAccessible().isAccessible()) { |
| if (usageInfo instanceof NonDelegatedMemberUsageInfo) { |
| final PsiElement nonDelegatedMember = ((NonDelegatedMemberUsageInfo)usageInfo).nonDelegatedMember; |
| HashSet<PsiElement> reportedContainers = reportedNonDelegatedUsages.get(nonDelegatedMember); |
| if (reportedContainers == null) { |
| reportedContainers = new HashSet<PsiElement>(); |
| reportedNonDelegatedUsages.put(nonDelegatedMember, reportedContainers); |
| } |
| final PsiElement container = ConflictsUtil.getContainer(element); |
| if (!reportedContainers.contains(container)) { |
| String message = RefactoringBundle.message("0.uses.1.of.an.instance.of.a.2", RefactoringUIUtil.getDescription(container, true), |
| RefactoringUIUtil.getDescription(nonDelegatedMember, true), classDescription); |
| conflicts.putValue(container, CommonRefactoringUtil.capitalize(message)); |
| reportedContainers.add(container); |
| } |
| } |
| else if (usageInfo instanceof UpcastedUsageInfo) { |
| final PsiClass upcastedTo = ((UpcastedUsageInfo)usageInfo).upcastedTo; |
| HashSet<PsiElement> reportedContainers = reportedUpcasts.get(upcastedTo); |
| if (reportedContainers == null) { |
| reportedContainers = new HashSet<PsiElement>(); |
| reportedUpcasts.put(upcastedTo, reportedContainers); |
| } |
| final PsiElement container = ConflictsUtil.getContainer(element); |
| if (!reportedContainers.contains(container)) { |
| String message = RefactoringBundle.message("0.upcasts.an.instance.of.1.to.2", |
| RefactoringUIUtil.getDescription(container, true), classDescription, |
| RefactoringUIUtil.getDescription(upcastedTo, false)); |
| conflicts.putValue(container, CommonRefactoringUtil.capitalize(message)); |
| reportedContainers.add(container); |
| } |
| } |
| } |
| } |
| else if (aUsage instanceof NoLongerOverridingSubClassMethodUsageInfo) { |
| NoLongerOverridingSubClassMethodUsageInfo info = (NoLongerOverridingSubClassMethodUsageInfo)aUsage; |
| String message = RefactoringBundle.message("0.will.no.longer.override.1", |
| RefactoringUIUtil.getDescription(info.getSubClassMethod(), true), |
| RefactoringUIUtil.getDescription(info.getOverridenMethod(), true)); |
| conflicts.putValue(info.getSubClassMethod(), message); |
| } |
| } |
| } |
| |
| private static ObjectUpcastedUsageInfo[] objectUpcastedUsages(UsageInfo[] usages) { |
| ArrayList<ObjectUpcastedUsageInfo> result = new ArrayList<ObjectUpcastedUsageInfo>(); |
| for (UsageInfo usage : usages) { |
| if (usage instanceof ObjectUpcastedUsageInfo) { |
| result.add(((ObjectUpcastedUsageInfo)usage)); |
| } |
| } |
| return result.toArray(new ObjectUpcastedUsageInfo[result.size()]); |
| } |
| |
| private ArrayList<UsageInfo> filterUsages(ArrayList<UsageInfo> usages) { |
| ArrayList<UsageInfo> result = new ArrayList<UsageInfo>(); |
| |
| for (UsageInfo usageInfo : usages) { |
| if (!(usageInfo instanceof InheritanceToDelegationUsageInfo)) { |
| continue; |
| } |
| if (usageInfo instanceof ObjectUpcastedUsageInfo) { |
| continue; |
| } |
| |
| if (!myIsDelegateOtherMembers) { |
| final FieldAccessibility delegateFieldAccessible = ((InheritanceToDelegationUsageInfo)usageInfo).getDelegateFieldAccessible(); |
| if (!delegateFieldAccessible.isAccessible()) continue; |
| } |
| |
| result.add(usageInfo); |
| } |
| return result; |
| } |
| |
| private void processClass(PsiClass inheritor, ArrayList<UsageInfo> usages) { |
| ClassReferenceScanner scanner = new ClassReferenceSearchingScanner(inheritor); |
| final MyClassInstanceReferenceVisitor instanceVisitor = new MyClassInstanceReferenceVisitor(inheritor, usages); |
| scanner.processReferences( |
| new ClassInstanceScanner(inheritor, |
| instanceVisitor) |
| ); |
| MyClassInheritorMemberReferencesVisitor classMemberVisitor = new MyClassInheritorMemberReferencesVisitor(inheritor, usages, instanceVisitor); |
| inheritor.accept(classMemberVisitor); |
| PsiSubstitutor inheritorSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(myClass, inheritor, PsiSubstitutor.EMPTY); |
| |
| PsiMethod[] methods = inheritor.getMethods(); |
| for (PsiMethod method : methods) { |
| final PsiMethod baseMethod = findSuperMethodInBaseClass(method); |
| |
| if (baseMethod != null) { |
| if (!baseMethod.hasModifierProperty(PsiModifier.ABSTRACT)) { |
| usages.add(new NoLongerOverridingSubClassMethodUsageInfo(method, baseMethod)); |
| } |
| else { |
| final PsiMethod[] methodsByName = myClass.findMethodsByName(method.getName(), false); |
| for (final PsiMethod classMethod : methodsByName) { |
| final MethodSignature signature = classMethod.getSignature(inheritorSubstitutor); |
| if (signature.equals(method.getSignature(PsiSubstitutor.EMPTY))) { |
| if (!classMethod.hasModifierProperty(PsiModifier.ABSTRACT)) { |
| usages.add(new NoLongerOverridingSubClassMethodUsageInfo(method, baseMethod)); |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| protected void performRefactoring(UsageInfo[] usages) { |
| try { |
| for (UsageInfo aUsage : usages) { |
| InheritanceToDelegationUsageInfo usage = (InheritanceToDelegationUsageInfo)aUsage; |
| |
| |
| if (usage instanceof UnqualifiedNonDelegatedMemberUsageInfo) { |
| delegateUsageFromClass(usage.getElement(), ((NonDelegatedMemberUsageInfo)usage).nonDelegatedMember, |
| usage.getDelegateFieldAccessible()); |
| } |
| else { |
| upcastToDelegation(usage.getElement(), usage.getDelegateFieldAccessible()); |
| } |
| } |
| |
| myAbstractDelegatedMethods = new HashSet<PsiMethod>(); |
| addInnerClass(); |
| addField(usages); |
| delegateMethods(); |
| addImplementingInterfaces(); |
| } catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| |
| private void addInnerClass() throws IncorrectOperationException { |
| if (!myIsInnerClassNeeded) return; |
| |
| PsiClass innerClass = myFactory.createClass(myInnerClassName); |
| final PsiSubstitutor superClassSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(myBaseClass, myClass, PsiSubstitutor.EMPTY); |
| final PsiClassType superClassType = myFactory.createType(myBaseClass, superClassSubstitutor); |
| final PsiJavaCodeReferenceElement baseClassReferenceElement = myFactory.createReferenceElementByType(superClassType); |
| if (!myBaseClass.isInterface()) { |
| innerClass.getExtendsList().add(baseClassReferenceElement); |
| } else { |
| innerClass.getImplementsList().add(baseClassReferenceElement); |
| } |
| PsiUtil.setModifierProperty(innerClass, PsiModifier.PRIVATE, true); |
| innerClass = (PsiClass) myClass.add(innerClass); |
| |
| List<InnerClassMethod> innerClassMethods = getInnerClassMethods(); |
| for (InnerClassMethod innerClassMethod : innerClassMethods) { |
| innerClassMethod.createMethod(innerClass); |
| } |
| } |
| |
| private void delegateUsageFromClass(PsiElement element, PsiElement nonDelegatedMember, |
| FieldAccessibility fieldAccessibility) throws IncorrectOperationException { |
| if (element instanceof PsiReferenceExpression) { |
| PsiReferenceExpression referenceExpression = (PsiReferenceExpression) element; |
| if (referenceExpression.getQualifierExpression() != null) { |
| upcastToDelegation(referenceExpression.getQualifierExpression(), fieldAccessibility); |
| } else { |
| final String name = ((PsiNamedElement) nonDelegatedMember).getName(); |
| final String qualifier; |
| if (isStatic (nonDelegatedMember)) { |
| qualifier = myBaseClass.getName(); |
| } |
| else if (!fieldAccessibility.isAccessible() && myGenerateGetter) { |
| qualifier = myGetterName + "()"; |
| } |
| else { |
| qualifier = myFieldName; |
| } |
| |
| PsiExpression newExpr = myFactory.createExpressionFromText(qualifier + "." + name, element); |
| newExpr = (PsiExpression) CodeStyleManager.getInstance(myProject).reformat(newExpr); |
| element.replace(newExpr); |
| } |
| } |
| else if (element instanceof PsiJavaCodeReferenceElement) { |
| final String name = ((PsiNamedElement) nonDelegatedMember).getName(); |
| |
| PsiElement parent = element.getParent (); |
| if (!isStatic (nonDelegatedMember) && parent instanceof PsiNewExpression) { |
| final PsiNewExpression newExpr = (PsiNewExpression) parent; |
| if (newExpr.getQualifier() != null) { |
| upcastToDelegation(newExpr.getQualifier(), fieldAccessibility); |
| } else { |
| final String qualifier; |
| if (!fieldAccessibility.isAccessible() && myGenerateGetter) { |
| qualifier = myGetterName + "()"; |
| } |
| else { |
| qualifier = myFieldName; |
| } |
| newExpr.replace(myFactory.createExpressionFromText(qualifier + "." + newExpr.getText(), parent)); |
| } |
| } |
| else { |
| final String qualifier = myBaseClass.getName(); |
| PsiJavaCodeReferenceElement newRef = myFactory.createFQClassNameReferenceElement(qualifier + "." + name, element.getResolveScope ()); |
| //newRef = (PsiJavaCodeReferenceElement) CodeStyleManager.getInstance(myProject).reformat(newRef); |
| element.replace(newRef); |
| } |
| } else { |
| LOG.assertTrue(false); |
| } |
| } |
| |
| private static boolean isStatic(PsiElement member) { |
| if (member instanceof PsiModifierListOwner) { |
| final PsiModifierListOwner method = (PsiModifierListOwner) member; |
| return method.hasModifierProperty (PsiModifier.STATIC); |
| } |
| return false; |
| } |
| |
| private void upcastToDelegation(PsiElement element, FieldAccessibility fieldAccessibility) throws IncorrectOperationException { |
| final PsiExpression expression = (PsiExpression) element; |
| |
| final PsiExpression newExpr; |
| final PsiReferenceExpression ref; |
| @NonNls final String delegateQualifier; |
| if (!(expression instanceof PsiThisExpression || expression instanceof PsiSuperExpression)) { |
| delegateQualifier = "a."; |
| } else { |
| PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(myProject).getResolveHelper(); |
| final PsiVariable psiVariable = resolveHelper.resolveReferencedVariable(myFieldName, element); |
| if (psiVariable == null) { |
| delegateQualifier = ""; |
| } else { |
| delegateQualifier = "a."; |
| } |
| } |
| if (!fieldAccessibility.isAccessible() && myGenerateGetter) { |
| newExpr = myFactory.createExpressionFromText(delegateQualifier + myGetterName + "()", expression); |
| ref = (PsiReferenceExpression) ((PsiMethodCallExpression) newExpr).getMethodExpression().getQualifierExpression(); |
| } else { |
| newExpr = myFactory.createExpressionFromText(delegateQualifier + myFieldName, expression); |
| ref = (PsiReferenceExpression) ((PsiReferenceExpression) newExpr).getQualifierExpression(); |
| } |
| // LOG.debug("upcastToDelegation:" + element + ":newExpr = " + newExpr); |
| // LOG.debug("upcastToDelegation:" + element + ":ref = " + ref); |
| if (ref != null) { |
| ref.replace(expression); |
| } |
| expression.replace(newExpr); |
| // LOG.debug("upcastToDelegation:" + element + ":replaced = " + replaced); |
| } |
| |
| private void delegateMethods() throws IncorrectOperationException { |
| for (PsiMethod method : myDelegatedMethods) { |
| if (!myAbstractDelegatedMethods.contains(method)) { |
| PsiMethod methodToAdd = delegateMethod(myFieldName, method, getSuperSubstitutor(method.getContainingClass())); |
| |
| String visibility = myDelegatedMethodsVisibility.get(method); |
| if (visibility != null) { |
| PsiUtil.setModifierProperty(methodToAdd, visibility, true); |
| } |
| |
| myClass.add(methodToAdd); |
| } |
| } |
| } |
| |
| private PsiMethod delegateMethod(@NonNls String delegationTarget, |
| PsiMethod method, |
| PsiSubstitutor substitutor) throws IncorrectOperationException { |
| substitutor = OverrideImplementUtil.correctSubstitutor(method, substitutor); |
| PsiMethod methodToAdd = GenerateMembersUtil.substituteGenericMethod(method, substitutor); |
| |
| final PsiModifierList modifierList = methodToAdd.getModifierList(); |
| final NullableNotNullManager manager = NullableNotNullManager.getInstance(myProject); |
| modifierList.setModifierProperty(PsiModifier.ABSTRACT, false); |
| final String nullable = manager.getNullable(method); |
| if (nullable != null) { |
| modifierList.addAfter(myFactory.createAnnotationFromText("@" + nullable, methodToAdd), null); |
| } |
| else { |
| final String notNull = manager.getNotNull(method); |
| if (notNull != null) { |
| modifierList.addAfter(myFactory.createAnnotationFromText("@" + notNull, methodToAdd), null); |
| } |
| } |
| |
| final String delegationBody = getDelegationBody(methodToAdd, delegationTarget); |
| PsiCodeBlock newBody = myFactory.createCodeBlockFromText(delegationBody, method); |
| |
| PsiCodeBlock oldBody = methodToAdd.getBody(); |
| if (oldBody != null) { |
| oldBody.replace(newBody); |
| } |
| else { |
| methodToAdd.addBefore(newBody, null); |
| } |
| |
| if (methodToAdd.getDocComment() != null) methodToAdd.getDocComment().delete(); |
| methodToAdd = (PsiMethod)CodeStyleManager.getInstance(myProject).reformat(methodToAdd); |
| methodToAdd = (PsiMethod)JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(methodToAdd); |
| return methodToAdd; |
| } |
| |
| private static String getDelegationBody(PsiMethod methodToAdd, String delegationTarget) { |
| @NonNls final StringBuffer buffer = new StringBuffer(); |
| buffer.append("{\n"); |
| |
| if (methodToAdd.getReturnType() != PsiType.VOID) { |
| buffer.append("return "); |
| } |
| |
| buffer.append(delegationTarget); |
| buffer.append("."); |
| buffer.append(methodToAdd.getName()); |
| buffer.append("("); |
| PsiParameter[] params = methodToAdd.getParameterList().getParameters(); |
| for (int i = 0; i < params.length; i++) { |
| PsiParameter param = params[i]; |
| if (i > 0) { |
| buffer.append(","); |
| } |
| buffer.append(param.getName()); |
| } |
| buffer.append(");\n}"); |
| return buffer.toString(); |
| } |
| |
| private void addImplementingInterfaces() throws IncorrectOperationException { |
| final PsiReferenceList implementsList = myClass.getImplementsList(); |
| LOG.assertTrue(implementsList != null); |
| for (PsiClass delegatedInterface : myDelegatedInterfaces) { |
| if (!myClassImplementedInterfaces.contains(delegatedInterface)) { |
| implementsList.add(myFactory.createClassReferenceElement(delegatedInterface)); |
| } |
| } |
| |
| if (!myBaseClass.isInterface()) { |
| final PsiReferenceList extendsList = myClass.getExtendsList(); |
| LOG.assertTrue(extendsList != null); |
| extendsList.getReferenceElements()[0].delete(); |
| } else { |
| final PsiJavaCodeReferenceElement[] interfaceRefs = implementsList.getReferenceElements(); |
| for (PsiJavaCodeReferenceElement interfaceRef : interfaceRefs) { |
| final PsiElement resolved = interfaceRef.resolve(); |
| if (myManager.areElementsEquivalent(myBaseClass, resolved)) { |
| interfaceRef.delete(); |
| break; |
| } |
| } |
| } |
| } |
| |
| private void addField(UsageInfo[] usages) throws IncorrectOperationException { |
| final String fieldVisibility = getFieldVisibility(usages); |
| |
| final boolean fieldInitializerNeeded = isFieldInitializerNeeded(); |
| |
| PsiField field = createField(fieldVisibility, fieldInitializerNeeded, defaultClassFieldType()); |
| |
| if (!myIsInnerClassNeeded) { |
| field.getTypeElement().replace(myFactory.createTypeElement(myBaseClassType)); |
| if (fieldInitializerNeeded) { |
| final PsiJavaCodeReferenceElement classReferenceElement = myFactory.createReferenceElementByType(myBaseClassType); |
| PsiNewExpression newExpression = (PsiNewExpression) field.getInitializer(); |
| newExpression.getClassReference().replace(classReferenceElement); |
| } |
| } |
| |
| field = (PsiField) CodeStyleManager.getInstance(myProject).reformat(field); |
| myClass.add(field); |
| if (!fieldInitializerNeeded) { |
| fixConstructors(); |
| } |
| |
| if (myGenerateGetter) { |
| final String getterVisibility = PsiModifier.PUBLIC; |
| @NonNls StringBuffer getterBuffer = new StringBuffer(); |
| getterBuffer.append(getterVisibility); |
| getterBuffer.append(" Object "); |
| getterBuffer.append(myGetterName); |
| getterBuffer.append("() {\n return "); |
| getterBuffer.append(myFieldName); |
| getterBuffer.append(";\n}"); |
| PsiMethod getter = myFactory.createMethodFromText(getterBuffer.toString(), myClass); |
| getter.getReturnTypeElement().replace(myFactory.createTypeElement(myBaseClassType)); |
| getter = (PsiMethod) CodeStyleManager.getInstance(myProject).reformat(getter); |
| myClass.add(getter); |
| } |
| } |
| |
| private String getFieldVisibility(UsageInfo[] usages) { |
| if (myIsDelegateOtherMembers && !myGenerateGetter) { |
| return PsiModifier.PUBLIC; |
| } |
| |
| for (UsageInfo aUsage : usages) { |
| InheritanceToDelegationUsageInfo usage = (InheritanceToDelegationUsageInfo)aUsage; |
| final FieldAccessibility delegateFieldAccessible = usage.getDelegateFieldAccessible(); |
| if (delegateFieldAccessible.isAccessible() && delegateFieldAccessible.getContainingClass() != myClass) { |
| return PsiModifier.PROTECTED; |
| } |
| } |
| return PsiModifier.PRIVATE; |
| } |
| |
| private @NonNls String defaultClassFieldType() { |
| return (myIsInnerClassNeeded ? myInnerClassName : "Object"); |
| } |
| |
| private PsiField createField(final String fieldVisibility, final boolean fieldInitializerNeeded, String defaultTypeName) throws IncorrectOperationException { |
| @NonNls StringBuffer buffer = new StringBuffer(); |
| buffer.append(fieldVisibility); |
| buffer.append(" final " + defaultTypeName + " "); |
| buffer.append(myFieldName); |
| if (fieldInitializerNeeded) { |
| buffer.append(" = new " + defaultTypeName + "()"); |
| } |
| buffer.append(";"); |
| return myFactory.createFieldFromText(buffer.toString(), myClass); |
| } |
| |
| private void fixConstructors() throws IncorrectOperationException { |
| if (myBaseClass.isInterface()) return; |
| final PsiJavaCodeReferenceElement baseClassReference = myFactory.createClassReferenceElement(myBaseClass); |
| |
| PsiMethod[] constructors = myClass.getConstructors(); |
| for (PsiMethod constructor : constructors) { |
| PsiCodeBlock body = constructor.getBody(); |
| final PsiStatement[] statements = body.getStatements(); |
| @NonNls String fieldQualifier = ""; |
| PsiParameter[] constructorParams = constructor.getParameterList().getParameters(); |
| for (PsiParameter constructorParam : constructorParams) { |
| if (myFieldName.equals(constructorParam.getName())) { |
| fieldQualifier = "this."; |
| break; |
| } |
| } |
| final @NonNls String assignmentText = fieldQualifier + myFieldName + "= new " + defaultClassFieldType() + "()"; |
| if (statements.length < 1 || !JavaHighlightUtil.isSuperOrThisCall(statements[0], true, true) || myBaseClass.isInterface()) { |
| PsiExpressionStatement assignmentStatement = |
| (PsiExpressionStatement)myFactory.createStatementFromText( |
| assignmentText, body |
| ); |
| if (!myIsInnerClassNeeded) { |
| final PsiAssignmentExpression assignmentExpr = (PsiAssignmentExpression)assignmentStatement.getExpression(); |
| final PsiNewExpression newExpression = (PsiNewExpression)assignmentExpr.getRExpression(); |
| assert newExpression != null; |
| final PsiJavaCodeReferenceElement classRef = newExpression.getClassReference(); |
| assert classRef != null; |
| classRef.replace(baseClassReference); |
| } |
| |
| assignmentStatement = (PsiExpressionStatement)CodeStyleManager.getInstance(myProject).reformat(assignmentStatement); |
| if (statements.length > 0) { |
| if (!JavaHighlightUtil.isSuperOrThisCall(statements[0], true, false)) { |
| body.addBefore(assignmentStatement, statements[0]); |
| } |
| else { |
| body.addAfter(assignmentStatement, statements[0]); |
| } |
| } |
| else { |
| body.add(assignmentStatement); |
| } |
| } |
| else { |
| final PsiExpressionStatement callStatement = ((PsiExpressionStatement)statements[0]); |
| if (!JavaHighlightUtil.isSuperOrThisCall(callStatement, false, true)) { |
| final PsiMethodCallExpression superConstructorCall = |
| (PsiMethodCallExpression)callStatement.getExpression(); |
| PsiAssignmentExpression assignmentExpression = |
| (PsiAssignmentExpression)myFactory.createExpressionFromText( |
| assignmentText, superConstructorCall |
| ); |
| PsiNewExpression newExpression = |
| (PsiNewExpression)assignmentExpression.getRExpression(); |
| if (!myIsInnerClassNeeded) { |
| newExpression.getClassReference().replace(baseClassReference); |
| } |
| assignmentExpression = (PsiAssignmentExpression)CodeStyleManager.getInstance(myProject).reformat(assignmentExpression); |
| newExpression.getArgumentList().replace(superConstructorCall.getArgumentList()); |
| superConstructorCall.replace(assignmentExpression); |
| } |
| } |
| } |
| } |
| |
| private boolean isFieldInitializerNeeded() { |
| if (myBaseClass.isInterface()) return true; |
| PsiMethod[] constructors = myClass.getConstructors(); |
| for (PsiMethod constructor : constructors) { |
| final PsiStatement[] statements = constructor.getBody().getStatements(); |
| if (statements.length > 0 && JavaHighlightUtil.isSuperOrThisCall(statements[0], true, false)) return false; |
| } |
| return true; |
| } |
| |
| private List<InnerClassMethod> getInnerClassMethods() { |
| ArrayList<InnerClassMethod> result = new ArrayList<InnerClassMethod>(); |
| |
| // find all neccessary constructors |
| if (!myBaseClass.isInterface()) { |
| PsiMethod[] constructors = myClass.getConstructors(); |
| for (PsiMethod constructor : constructors) { |
| final PsiStatement[] statements = constructor.getBody().getStatements(); |
| if (statements.length > 0 && JavaHighlightUtil.isSuperOrThisCall(statements[0], true, false)) { |
| final PsiMethodCallExpression superConstructorCall = |
| (PsiMethodCallExpression)((PsiExpressionStatement)statements[0]).getExpression(); |
| PsiElement superConstructor = superConstructorCall.getMethodExpression().resolve(); |
| if (superConstructor instanceof PsiMethod && ((PsiMethod)superConstructor).isConstructor()) { |
| result.add(new InnerClassConstructor((PsiMethod)superConstructor)); |
| } |
| } |
| } |
| } |
| |
| // find overriding/implementing method |
| { |
| class InnerClassOverridingMethod extends InnerClassMethod { |
| public InnerClassOverridingMethod(PsiMethod method) { |
| super(method); |
| } |
| |
| public void createMethod(PsiClass innerClass) |
| throws IncorrectOperationException { |
| OverriddenMethodClassMemberReferencesVisitor visitor = new OverriddenMethodClassMemberReferencesVisitor(); |
| myClass.accept(visitor); |
| final List<PsiAction> actions = visitor.getPsiActions(); |
| for (PsiAction action : actions) { |
| action.run(); |
| } |
| innerClass.add(myMethod); |
| myMethod.delete(); |
| // myMethod.replace(delegateMethod(myMethod)); |
| } |
| } |
| |
| for (PsiMethod method : myOverriddenMethods) { |
| result.add(new InnerClassOverridingMethod(method)); |
| } |
| } |
| |
| // fix abstract methods |
| { |
| class InnerClassAbstractMethod extends InnerClassMethod { |
| private final boolean myImplicitImplementation; |
| |
| public InnerClassAbstractMethod(PsiMethod method, final boolean implicitImplementation) { |
| super(method); |
| myImplicitImplementation = implicitImplementation; |
| } |
| |
| public void createMethod(PsiClass innerClass) |
| throws IncorrectOperationException { |
| PsiSubstitutor substitutor = getSuperSubstitutor(myMethod.getContainingClass()); |
| PsiMethod method = delegateMethod(myClass.getName() + ".this", myMethod, substitutor); |
| final PsiClass containingClass = myMethod.getContainingClass(); |
| if (myBaseClass.isInterface() || containingClass.isInterface()) { |
| PsiUtil.setModifierProperty(method, PsiModifier.PUBLIC, true); |
| } |
| innerClass.add(method); |
| if (!myImplicitImplementation) { |
| final MethodSignature signature = myMethod.getSignature(substitutor); |
| PsiMethod outerMethod = MethodSignatureUtil.findMethodBySignature(myClass, signature, false); |
| if (outerMethod == null) { |
| String visibility = checkOuterClassAbstractMethod(signature); |
| PsiMethod newOuterMethod = (PsiMethod)myClass.add(myMethod); |
| PsiUtil.setModifierProperty(newOuterMethod, visibility, true); |
| final PsiDocComment docComment = newOuterMethod.getDocComment(); |
| if (docComment != null) { |
| docComment.delete(); |
| } |
| } |
| } |
| } |
| |
| } |
| PsiMethod[] methods = myBaseClass.getAllMethods(); |
| |
| for (PsiMethod method : methods) { |
| if (method.hasModifierProperty(PsiModifier.ABSTRACT)) { |
| final MethodSignature signature = method.getSignature(getSuperSubstitutor(method.getContainingClass())); |
| PsiMethod classMethod = MethodSignatureUtil.findMethodBySignature(myClass, signature, true); |
| if (classMethod == null || classMethod.hasModifierProperty(PsiModifier.ABSTRACT)) { |
| result.add(new InnerClassAbstractMethod(method, false)); |
| } |
| else if ((myBaseClass.isInterface() && classMethod.getContainingClass() != myClass)) { // IDEADEV-19675 |
| result.add(new InnerClassAbstractMethod(method, true)); |
| } |
| } |
| } |
| } |
| |
| |
| return result; |
| } |
| |
| private void showObjectUpcastedUsageView(final ObjectUpcastedUsageInfo[] usages) { |
| UsageViewPresentation presentation = new UsageViewPresentation(); |
| presentation.setTargetsNodeText(RefactoringBundle.message("replacing.inheritance.with.delegation")); |
| presentation.setCodeUsagesString(RefactoringBundle.message("instances.casted.to.java.lang.object")); |
| final String upcastedString = RefactoringBundle.message("instances.upcasted.to.object"); |
| presentation.setUsagesString(upcastedString); |
| presentation.setTabText(upcastedString); |
| |
| UsageViewManager manager = UsageViewManager.getInstance(myProject); |
| manager.showUsages( |
| new UsageTarget[]{new PsiElement2UsageTargetAdapter(myClass)}, |
| UsageInfoToUsageConverter.convert(new PsiElement[]{myClass}, usages), |
| presentation |
| ); |
| |
| WindowManager.getInstance().getStatusBar(myProject).setInfo(RefactoringBundle.message("instances.upcasted.to.java.lang.object.found")); |
| } |
| |
| /** |
| * |
| * @param methodSignature |
| * @return Visibility |
| */ |
| @PsiModifier.ModifierConstant |
| private String checkOuterClassAbstractMethod(MethodSignature methodSignature) { |
| String visibility = PsiModifier.PROTECTED; |
| for (PsiMethod method : myDelegatedMethods) { |
| MethodSignature otherSignature = method.getSignature(getSuperSubstitutor(method.getContainingClass())); |
| |
| if (MethodSignatureUtil.areSignaturesEqual(otherSignature, methodSignature)) { |
| visibility = VisibilityUtil.getHighestVisibility(visibility, |
| VisibilityUtil.getVisibilityModifier(method.getModifierList())); |
| myAbstractDelegatedMethods.add(method); |
| } |
| } |
| return visibility; |
| } |
| |
| private Set<PsiMethod> getOverriddenMethods() { |
| LinkedHashSet<PsiMethod> result = new LinkedHashSet<PsiMethod>(); |
| |
| PsiMethod[] methods = myClass.getMethods(); |
| for (PsiMethod method : methods) { |
| if (findSuperMethodInBaseClass(method) != null) result.add(method); |
| } |
| return result; |
| } |
| |
| @Nullable |
| private PsiMethod findSuperMethodInBaseClass (PsiMethod method) { |
| final PsiMethod[] superMethods = method.findSuperMethods(); |
| for (PsiMethod superMethod : superMethods) { |
| PsiClass containingClass = superMethod.getContainingClass(); |
| if (InheritanceUtil.isInheritorOrSelf(myBaseClass, containingClass, true)) { |
| String qName = containingClass.getQualifiedName(); |
| if (qName == null || !CommonClassNames.JAVA_LANG_OBJECT.equals(qName)) { |
| return superMethod; |
| } |
| } |
| } |
| return null; |
| } |
| |
| |
| protected String getCommandName() { |
| return RefactoringBundle.message("replace.inheritance.with.delegation.command", DescriptiveNameUtil.getDescriptiveName(myClass)); |
| } |
| |
| private Set<PsiMember> getAllBaseClassMembers() { |
| HashSet<PsiMember> result = new HashSet<PsiMember>(); |
| addAll(result, myBaseClass.getAllFields()); |
| addAll(result, myBaseClass.getAllInnerClasses()); |
| addAll(result, myBaseClass.getAllMethods()); |
| |
| //remove java.lang.Object members |
| for (Iterator<PsiMember> iterator = result.iterator(); iterator.hasNext();) { |
| PsiMember member = iterator.next(); |
| if (CommonClassNames.JAVA_LANG_OBJECT.equals(member.getContainingClass().getQualifiedName())) { |
| iterator.remove(); |
| } |
| } |
| return Collections.unmodifiableSet(result); |
| } |
| |
| private Set<PsiClass> getAllBases() { |
| HashSet<PsiClass> temp = new HashSet<PsiClass>(); |
| InheritanceUtil.getSuperClasses(myBaseClass, temp, true); |
| temp.add(myBaseClass); |
| return Collections.unmodifiableSet(temp); |
| } |
| |
| private static <T> void addAll(Collection<T> collection, T[] objs) { |
| for (T obj : objs) { |
| collection.add(obj); |
| } |
| } |
| |
| private boolean isDelegated(PsiMember classMember) { |
| if(!(classMember instanceof PsiMethod)) return false; |
| final PsiMethod method = (PsiMethod) classMember; |
| for (PsiMethod delegatedMethod : myDelegatedMethods) { |
| //methods reside in base class, so no substitutor needed |
| if (MethodSignatureUtil.areSignaturesEqual(method.getSignature(PsiSubstitutor.EMPTY), |
| delegatedMethod.getSignature(PsiSubstitutor.EMPTY))) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private class MyClassInheritorMemberReferencesVisitor extends ClassMemberReferencesVisitor { |
| private final List<UsageInfo> myUsageInfoStorage; |
| private final ClassInstanceScanner.ClassInstanceReferenceVisitor myInstanceVisitor; |
| |
| MyClassInheritorMemberReferencesVisitor(PsiClass aClass, List<UsageInfo> usageInfoStorage, |
| ClassInstanceScanner.ClassInstanceReferenceVisitor instanceScanner) { |
| super(aClass); |
| |
| myUsageInfoStorage = usageInfoStorage; |
| myInstanceVisitor = instanceScanner; |
| } |
| |
| protected void visitClassMemberReferenceElement(PsiMember classMember, PsiJavaCodeReferenceElement classMemberReference) { |
| if ("super".equals(classMemberReference.getText()) && classMemberReference.getParent() instanceof PsiMethodCallExpression) { |
| return; |
| } |
| |
| if (classMember != null && myBaseClassMembers.contains(classMember) && !isDelegated(classMember)) { |
| final FieldAccessibility delegateFieldVisibility = new FieldAccessibility(true, getPsiClass()); |
| final InheritanceToDelegationUsageInfo usageInfo; |
| if (classMemberReference instanceof PsiReferenceExpression) { |
| if (((PsiReferenceExpression) classMemberReference).getQualifierExpression() == null) { |
| usageInfo = new UnqualifiedNonDelegatedMemberUsageInfo(classMemberReference, classMember, |
| delegateFieldVisibility); |
| } else { |
| usageInfo = new NonDelegatedMemberUsageInfo( |
| ((PsiReferenceExpression) classMemberReference).getQualifierExpression(), |
| classMember, delegateFieldVisibility |
| ); |
| } |
| myUsageInfoStorage.add(usageInfo); |
| } |
| else /*if (classMemberReference instanceof PsiJavaCodeReferenceElement)*/ { |
| usageInfo = new UnqualifiedNonDelegatedMemberUsageInfo(classMemberReference, classMember, |
| delegateFieldVisibility); |
| myUsageInfoStorage.add(usageInfo); |
| |
| } |
| } |
| } |
| |
| @Override public void visitThisExpression(PsiThisExpression expression) { |
| ClassInstanceScanner.processNonArrayExpression(myInstanceVisitor, expression, null); |
| } |
| } |
| |
| private class MyClassMemberReferencesVisitor extends MyClassInheritorMemberReferencesVisitor { |
| MyClassMemberReferencesVisitor(List<UsageInfo> usageInfoStorage, |
| ClassInstanceScanner.ClassInstanceReferenceVisitor instanceScanner) { |
| super(InheritanceToDelegationProcessor.this.myClass, usageInfoStorage, instanceScanner); |
| } |
| |
| @Override public void visitMethod(PsiMethod method) { |
| if (!myOverriddenMethods.contains(method)) { |
| super.visitMethod(method); |
| } |
| } |
| } |
| |
| interface PsiAction { |
| void run() throws IncorrectOperationException; |
| } |
| |
| /** |
| * This visitor should be called for overriden methods before they are moved to an inner class |
| */ |
| private class OverriddenMethodClassMemberReferencesVisitor extends ClassMemberReferencesVisitor { |
| private final ArrayList<PsiAction> myPsiActions; |
| private final PsiThisExpression myQualifiedThis; |
| |
| OverriddenMethodClassMemberReferencesVisitor() throws IncorrectOperationException { |
| super(myClass); |
| myPsiActions = new ArrayList<PsiAction>(); |
| final PsiJavaCodeReferenceElement classReferenceElement = myFactory.createClassReferenceElement(myClass); |
| myQualifiedThis = (PsiThisExpression) myFactory.createExpressionFromText("A.this", null); |
| myQualifiedThis.getQualifier().replace(classReferenceElement); |
| } |
| |
| public List<PsiAction> getPsiActions() { |
| return myPsiActions; |
| } |
| |
| class QualifyThis implements PsiAction { |
| private final PsiThisExpression myThisExpression; |
| |
| QualifyThis(PsiThisExpression thisExpression) { |
| myThisExpression = thisExpression; |
| } |
| |
| public void run() throws IncorrectOperationException { |
| myThisExpression.replace(myQualifiedThis); |
| } |
| } |
| |
| class QualifyName implements PsiAction { |
| private final PsiReferenceExpression myRef; |
| private final String myReferencedName; |
| |
| QualifyName(PsiReferenceExpression ref, String name) { |
| myRef = ref; |
| myReferencedName = name; |
| } |
| |
| public void run() throws IncorrectOperationException { |
| PsiReferenceExpression newRef = |
| (PsiReferenceExpression) myFactory.createExpressionFromText("a." + myReferencedName, null); |
| newRef.getQualifierExpression().replace(myQualifiedThis); |
| myRef.replace(newRef); |
| } |
| } |
| |
| class QualifyWithField implements PsiAction { |
| private final PsiReferenceExpression myReference; |
| private final String myReferencedName; |
| |
| public QualifyWithField(final PsiReferenceExpression reference, final String name) { |
| myReference = reference; |
| myReferencedName = name; |
| } |
| |
| public void run() throws IncorrectOperationException { |
| PsiReferenceExpression newRef = |
| (PsiReferenceExpression) myFactory.createExpressionFromText(myFieldName + "." + myReferencedName, null); |
| myReference.replace(newRef); |
| } |
| } |
| |
| protected void visitClassMemberReferenceExpression(PsiMember classMember, |
| PsiReferenceExpression classMemberReference) { |
| if (classMember instanceof PsiField) { |
| final PsiField field = (PsiField) classMember; |
| |
| if (field.getContainingClass().equals(myClass)) { |
| final String name = field.getName(); |
| final PsiField baseField = myBaseClass.findFieldByName(name, true); |
| if (baseField != null) { |
| myPsiActions.add(new QualifyName(classMemberReference, name)); |
| } else if (classMemberReference.getQualifierExpression() instanceof PsiThisExpression) { |
| myPsiActions.add(new QualifyThis((PsiThisExpression) classMemberReference.getQualifierExpression())); |
| } |
| } |
| } else if (classMember instanceof PsiMethod) { |
| final PsiMethod method = (PsiMethod) classMember; |
| |
| if (method.getContainingClass().equals(myClass)) { |
| if (!myOverriddenMethods.contains(method)) { |
| final PsiMethod baseMethod = findSuperMethodInBaseClass(method); |
| if (baseMethod != null) { |
| myPsiActions.add(new QualifyName(classMemberReference, baseMethod.getName())); |
| } else if (classMemberReference.getQualifierExpression() instanceof PsiThisExpression) { |
| myPsiActions.add(new QualifyThis((PsiThisExpression) classMemberReference.getQualifierExpression())); |
| } |
| } |
| else if (!myDelegatedMethods.contains(method)) { |
| myPsiActions.add(new QualifyWithField(classMemberReference, method.getName())); |
| } |
| } |
| } |
| } |
| |
| @Override public void visitThisExpression(final PsiThisExpression expression) { |
| class Visitor implements ClassInstanceScanner.ClassInstanceReferenceVisitor { |
| public void visitQualifier(PsiReferenceExpression qualified, PsiExpression instanceRef, PsiElement referencedInstance) { |
| LOG.assertTrue(false); |
| } |
| |
| public void visitTypeCast(PsiTypeCastExpression typeCastExpression, PsiExpression instanceRef, PsiElement referencedInstance) { |
| processType(typeCastExpression.getCastType().getType()); |
| } |
| |
| public void visitReadUsage(PsiExpression instanceRef, PsiType expectedType, PsiElement referencedInstance) { |
| processType(expectedType); |
| } |
| |
| public void visitWriteUsage(PsiExpression instanceRef, PsiType assignedType, PsiElement referencedInstance) { |
| LOG.assertTrue(false); |
| } |
| |
| private void processType(PsiType type) { |
| final PsiClass resolved = PsiUtil.resolveClassInType(type); |
| if (resolved != null && !myBaseClassBases.contains(resolved)) { |
| myPsiActions.add(new QualifyThis(expression)); |
| } |
| } |
| } |
| Visitor visitor = new Visitor(); |
| ClassInstanceScanner.processNonArrayExpression(visitor, expression, null); |
| } |
| |
| protected void visitClassMemberReferenceElement(PsiMember classMember, PsiJavaCodeReferenceElement classMemberReference) { |
| } |
| |
| } |
| |
| |
| private final class MyClassInstanceReferenceVisitor implements ClassInstanceScanner.ClassInstanceReferenceVisitor { |
| private final PsiClass myClass; |
| private final List<UsageInfo> myUsageInfoStorage; |
| private final Set<PsiClass> myImplementedInterfaces; |
| |
| public MyClassInstanceReferenceVisitor(PsiClass aClass, List<UsageInfo> usageInfoStorage) { |
| myClass = aClass; |
| myUsageInfoStorage = usageInfoStorage; |
| myImplementedInterfaces = getImplementedInterfaces(); |
| } |
| |
| public Set<PsiClass> getImplementedInterfaces() { |
| PsiClass aClass = myClass; |
| HashSet<PsiClass> result = new HashSet<PsiClass>(); |
| while (aClass != null && !myManager.areElementsEquivalent(aClass, myBaseClass)) { |
| final PsiClassType[] implementsTypes = aClass.getImplementsListTypes(); |
| for (PsiClassType implementsType : implementsTypes) { |
| PsiClass resolved = implementsType.resolve(); |
| if (resolved != null && !myManager.areElementsEquivalent(resolved, myBaseClass)) { |
| result.add(resolved); |
| InheritanceUtil.getSuperClasses(resolved, result, true); |
| } |
| } |
| |
| aClass = aClass.getSuperClass(); |
| } |
| return result; |
| } |
| |
| |
| public void visitQualifier(PsiReferenceExpression qualified, PsiExpression instanceRef, PsiElement referencedInstance) { |
| final PsiExpression qualifierExpression = qualified.getQualifierExpression(); |
| |
| // do not add usages inside a class |
| if (qualifierExpression == null |
| || qualifierExpression instanceof PsiThisExpression |
| || qualifierExpression instanceof PsiSuperExpression) { |
| return; |
| } |
| |
| PsiElement resolved = qualified.resolve(); |
| if (resolved != null && (myBaseClassMembers.contains(resolved) || myOverriddenMethods.contains(resolved)) |
| && !isDelegated((PsiMember)resolved)) { |
| myUsageInfoStorage.add(new NonDelegatedMemberUsageInfo(instanceRef, resolved, getFieldAccessibility(instanceRef))); |
| } |
| } |
| |
| public void visitTypeCast(PsiTypeCastExpression typeCastExpression, PsiExpression instanceRef, PsiElement referencedInstance) { |
| processTypedUsage(typeCastExpression.getCastType().getType(), instanceRef); |
| } |
| |
| |
| public void visitReadUsage(PsiExpression instanceRef, PsiType expectedType, PsiElement referencedInstance) { |
| processTypedUsage(expectedType, instanceRef); |
| } |
| |
| public void visitWriteUsage(PsiExpression instanceRef, PsiType assignedType, PsiElement referencedInstance) { |
| } |
| |
| private void processTypedUsage(PsiType type, PsiExpression instanceRef) { |
| final PsiClass aClass = PsiUtil.resolveClassInType(type); |
| if (aClass == null) return; |
| String qName = aClass.getQualifiedName(); |
| if (qName != null && CommonClassNames.JAVA_LANG_OBJECT.equals(qName)) { |
| myUsageInfoStorage.add(new ObjectUpcastedUsageInfo(instanceRef, aClass, getFieldAccessibility(instanceRef))); |
| } else { |
| if (myBaseClassBases.contains(aClass) |
| && !myImplementedInterfaces.contains(aClass) && !myDelegatedInterfaces.contains(aClass)) { |
| myUsageInfoStorage.add(new UpcastedUsageInfo(instanceRef, aClass, getFieldAccessibility(instanceRef))); |
| } |
| } |
| } |
| } |
| } |