| /* |
| * 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.move; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.CodeStyleManager; |
| import com.intellij.psi.util.InheritanceUtil; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.containers.HashSet; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * @author ven |
| */ |
| public class MoveInstanceMembersUtil { |
| private static final Logger LOG = Logger.getInstance("#" + MoveInstanceMembersUtil.class.getName()); |
| |
| /** |
| * @param member nonstatic class member to search for class references in |
| * @return Set<PsiMember> in result map may be null in case no member is needed, but class itself is. |
| */ |
| public static Map<PsiClass, Set<PsiMember>> getThisClassesToMembers(final PsiMember member) { |
| Map<PsiClass, Set<PsiMember>> map = new LinkedHashMap<PsiClass, Set<PsiMember>>(); |
| getThisClassesToMembers (member, map, member); |
| return map; |
| } |
| |
| private static void getThisClassesToMembers(final PsiElement scope, final Map<PsiClass, Set<PsiMember>> map, final PsiMember refMember) { |
| if (scope instanceof PsiExpression) { |
| final PsiExpression expression = (PsiExpression)scope; |
| if (!(scope instanceof PsiReferenceExpression) || !((PsiReferenceExpression)scope).isReferenceTo(refMember)) { |
| final Pair<PsiMember, PsiClass> pair = getMemberAndClassReferencedByThis(expression); |
| if (pair != null) { |
| PsiClass refClass = pair.getSecond(); |
| PsiMember member = pair.getFirst(); |
| if (refClass != null) { |
| boolean inherited = false; |
| PsiClass parentClass = PsiTreeUtil.getParentOfType(scope, PsiClass.class, true); |
| while (parentClass != null && PsiTreeUtil.isAncestor(refMember, parentClass, false)) { |
| if (parentClass == refClass || parentClass.isInheritor(refClass, true)) { |
| inherited = true; |
| break; |
| } |
| parentClass = PsiTreeUtil.getParentOfType(parentClass, PsiClass.class, true); |
| } |
| if (!inherited && !PsiTreeUtil.isAncestor(refMember, member, false)) { |
| addReferencedMember(map, refClass, member); |
| } |
| } |
| } |
| |
| if (expression instanceof PsiThisExpression) { |
| final PsiJavaCodeReferenceElement thisQualifier = ((PsiThisExpression)expression).getQualifier(); |
| PsiClass thisClass = thisQualifier == null ? PsiTreeUtil.getParentOfType(expression, PsiClass.class, true) : ((PsiClass)thisQualifier.resolve()); |
| if (thisClass != null && !PsiTreeUtil.isAncestor( refMember,thisClass, false)) { |
| addReferencedMember(map, thisClass, null); |
| } |
| } |
| } |
| } |
| |
| final PsiElement[] children = scope.getChildren(); |
| for (PsiElement child : children) { |
| getThisClassesToMembers(child, map, refMember); |
| } |
| } |
| |
| private static void addReferencedMember(final Map<PsiClass, Set<PsiMember>> map, final PsiClass classReferenced, final PsiMember member) { |
| Set<PsiMember> members = map.get(classReferenced); |
| if (members == null) { |
| members = new HashSet<PsiMember>(); |
| map.put(classReferenced, members); |
| } |
| members.add(member); |
| } |
| |
| @Nullable |
| private static Pair<PsiMember, PsiClass> getMemberAndClassReferencedByThis(final PsiExpression expression) { |
| if (expression instanceof PsiReferenceExpression) { |
| final PsiExpression qualifier = ((PsiReferenceExpression)expression).getQualifierExpression(); |
| if (qualifier == null || qualifier instanceof PsiThisExpression) { |
| final PsiElement resolved = ((PsiReferenceExpression)expression).resolve(); |
| if (resolved instanceof PsiMember && !((PsiMember)resolved).hasModifierProperty(PsiModifier.STATIC)) { |
| PsiClass referencedClass = getReferencedClass((PsiMember)resolved, qualifier, expression); |
| return Pair.create((PsiMember)resolved, referencedClass); |
| } |
| } |
| } else if (expression instanceof PsiNewExpression) { |
| final PsiNewExpression newExpression = (PsiNewExpression)expression; |
| final PsiExpression qualifier = newExpression.getQualifier(); |
| if (qualifier == null || qualifier instanceof PsiThisExpression) { |
| PsiJavaCodeReferenceElement classReference = newExpression.getClassOrAnonymousClassReference(); |
| if (classReference != null) { |
| final PsiClass resolved = (PsiClass)classReference.resolve(); |
| if (resolved != null && !resolved.hasModifierProperty(PsiModifier.STATIC)) { |
| PsiClass referencedClass = getReferencedClass(resolved, qualifier, expression); |
| return new Pair<PsiMember, PsiClass>(resolved, referencedClass); |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| @Nullable |
| private static PsiClass getReferencedClass(final PsiMember member, final PsiExpression exprQualifier, final PsiExpression expression) { |
| if (exprQualifier != null) { |
| final PsiType type = exprQualifier.getType(); |
| if (type instanceof PsiClassType) { |
| return ((PsiClassType)type).resolve(); |
| } |
| return null; |
| } else { |
| PsiClass referencedClass = member.getContainingClass(); |
| if (referencedClass == null) return null; |
| final PsiClass parentClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class); |
| assert parentClass != null; |
| if (InheritanceUtil.isInheritorOrSelf(parentClass, referencedClass, false)) { |
| referencedClass = parentClass; |
| } |
| return referencedClass; |
| } |
| } |
| |
| @Nullable |
| public static PsiClass getClassReferencedByThis(final PsiExpression expression) { |
| PsiClass enclosingClass = PsiTreeUtil.getParentOfType(expression, PsiClass.class); |
| if (enclosingClass == null) return null; |
| final Pair<PsiMember, PsiClass> pair = getMemberAndClassReferencedByThis(expression); |
| if (pair != null) return pair.getSecond(); |
| |
| if (expression instanceof PsiThisExpression) { |
| final PsiJavaCodeReferenceElement thisQualifier = ((PsiThisExpression)expression).getQualifier(); |
| if (thisQualifier == null) { |
| return enclosingClass; |
| } |
| else { |
| return (PsiClass)thisQualifier.resolve(); |
| } |
| } |
| return null; |
| } |
| |
| public static void moveInitializerToConstructor(PsiElementFactory factory, PsiMethod constructor, PsiField field) { |
| final PsiExpression initializer = field.getInitializer(); |
| PsiExpression initializerCopy = (PsiExpression)initializer.copy(); |
| final PsiCodeBlock body = constructor.getBody(); |
| if (body != null) { |
| try { |
| String fieldName = field.getName(); |
| final PsiReferenceExpression refExpr = (PsiReferenceExpression)factory.createExpressionFromText(fieldName, body); |
| if (refExpr.resolve() != null) fieldName = "this." + fieldName; |
| PsiExpressionStatement statement = (PsiExpressionStatement)factory.createStatementFromText(fieldName + "= y;", null); |
| if (initializerCopy instanceof PsiArrayInitializerExpression) { |
| PsiType type = initializer.getType(); |
| PsiNewExpression newExpression = |
| (PsiNewExpression)factory.createExpressionFromText("new " + type.getCanonicalText() + "{}", body); |
| newExpression.getArrayInitializer().replace(initializerCopy); |
| initializerCopy = newExpression; |
| } |
| ((PsiAssignmentExpression)statement.getExpression()).getRExpression().replace(initializerCopy); |
| statement = (PsiExpressionStatement)CodeStyleManager.getInstance(field.getManager().getProject()).reformat(statement); |
| body.add(statement); |
| initializer.delete(); |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| } |
| } |