blob: 571710723547b14805644bc3590dfa0b13c99a9a [file] [log] [blame]
/*
* 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);
}
}
}
}