blob: b3a8846658cc230efec16383e69dfc9758f631ea [file] [log] [blame]
/*
* Copyright 2000-2011 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.typeMigration;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.util.containers.ContainerUtil;
import java.util.*;
/**
* @author anna
* Date: 19-Apr-2008
*/
public class ClassTypeArgumentMigrationProcessor {
private static final Logger LOG = Logger.getInstance("#" + ClassTypeArgumentMigrationProcessor.class.getName());
private final TypeMigrationLabeler myLabeler;
public ClassTypeArgumentMigrationProcessor(final TypeMigrationLabeler labeler) {
myLabeler = labeler;
}
public void migrateClassTypeParameter(final PsiReferenceParameterList referenceParameterList, final PsiType migrationType) {
final PsiClass psiClass = PsiTreeUtil.getParentOfType(referenceParameterList, PsiClass.class);
LOG.assertTrue(psiClass != null);
final PsiClass superClass = psiClass.getSuperClass();
LOG.assertTrue(superClass != null);
myLabeler.getTypeEvaluator().setType(new TypeMigrationUsageInfo(superClass), migrationType);
final Map<PsiElement, Pair<PsiReference[], PsiType>> roots = new HashMap<PsiElement, Pair<PsiReference[], PsiType>>();
markTypeParameterUsages(psiClass, migrationType, referenceParameterList, roots);
final Set<PsiElement> processed = new HashSet<PsiElement>();
for (Map.Entry<PsiElement, Pair<PsiReference[], PsiType>> entry : roots.entrySet()) {
final PsiElement member = entry.getKey();
final PsiType type = entry.getValue().second;
if (member instanceof PsiParameter && ((PsiParameter)member).getDeclarationScope() instanceof PsiMethod) {
myLabeler.migrateMethodCallExpressions(type, (PsiParameter)member, psiClass);
}
final PsiReference[] references = entry.getValue().first;
for (PsiReference usage : references) {
myLabeler.migrateRootUsageExpression(usage, processed);
}
}
}
private void markTypeParameterUsages(final PsiClass psiClass, PsiType migrationType, PsiReferenceParameterList referenceParameterList,
final Map<PsiElement, Pair<PsiReference[], PsiType>> roots) {
final Map<PsiClass, PsiTypeParameter[]> visibleTypeParams = getTypeParametersHierarchy(referenceParameterList);
final PsiSubstitutor substitutor = composeSubstitutor(psiClass.getProject(), migrationType, visibleTypeParams);
for (Map.Entry<PsiClass, PsiTypeParameter[]> entry : visibleTypeParams.entrySet()) {
final TypeParameterSearcher parameterSearcher = new TypeParameterSearcher(entry.getValue());
entry.getKey().accept(new JavaRecursiveElementVisitor(){
@Override
public void visitMethod(final PsiMethod method) {
super.visitMethod(method);
processMemberType(method, parameterSearcher, psiClass, substitutor, roots);
for (PsiParameter parameter : method.getParameterList().getParameters()) {
processMemberType(parameter, parameterSearcher, psiClass, substitutor, roots);
}
}
@Override
public void visitField(final PsiField field) {
super.visitField(field);
processMemberType(field, parameterSearcher, psiClass, substitutor, roots);
}
});
}
}
private void processMemberType(final PsiElement element,
final TypeParameterSearcher parameterSearcher,
final PsiClass psiClass,
final PsiSubstitutor substitutor,
final Map<PsiElement, Pair<PsiReference[], PsiType>> roots) {
final PsiType elementType = TypeMigrationLabeler.getElementType(element);
if (elementType != null && elementType.accept(parameterSearcher).booleanValue()) {
final PsiType memberType = substitutor.substitute(elementType);
prepareMethodsChangeSignature(psiClass, element, memberType);
final List<PsiReference> refs = TypeMigrationLabeler.filterReferences(psiClass, ReferencesSearch.search(element, psiClass.getUseScope()));
roots.put(element, Pair.create(myLabeler.markRootUsages(element, memberType, refs.toArray(new PsiReference[refs.size()])), memberType));
}
}
private static PsiSubstitutor composeSubstitutor(final Project project, final PsiType migrationType, final Map<PsiClass, PsiTypeParameter[]> visibleTypeParams) {
PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
final PsiResolveHelper psiResolveHelper = JavaPsiFacade.getInstance(project).getResolveHelper();
for (Map.Entry<PsiClass,PsiTypeParameter[]> entry : visibleTypeParams.entrySet()) {
final PsiClassType clearedOriginalType = JavaPsiFacade.getElementFactory(project).createType(entry.getKey(), PsiSubstitutor.EMPTY);
for (PsiTypeParameter parameter : entry.getValue()) {
substitutor = substitutor.put(parameter,
psiResolveHelper.getSubstitutionForTypeParameter(parameter, clearedOriginalType, migrationType, true, clearedOriginalType.getLanguageLevel()));
}
}
return substitutor;
}
private static Map<PsiClass, PsiTypeParameter[]> getTypeParametersHierarchy(final PsiReferenceParameterList referenceParameterList) {
final PsiElement parent = referenceParameterList.getParent();
LOG.assertTrue(parent instanceof PsiJavaCodeReferenceElement);
final PsiClass superClass = (PsiClass)((PsiJavaCodeReferenceElement)parent).resolve();
LOG.assertTrue(superClass != null);
final Map<PsiClass, PsiTypeParameter[]> visibleTypeParams = new HashMap<PsiClass, PsiTypeParameter[]>();
visibleTypeParams.put(superClass, superClass.getTypeParameters());
final HashSet<PsiClass> superClasses = new HashSet<PsiClass>();
InheritanceUtil.getSuperClasses(superClass, superClasses, true);
for (PsiClass superSuperClass : superClasses) {
visibleTypeParams.put(superSuperClass, superSuperClass.getTypeParameters());
}
return visibleTypeParams;
}
/**
* signature should be changed for methods with type parameters
*/
private void prepareMethodsChangeSignature(final PsiClass currentClass, final PsiElement memberToChangeSignature, final PsiType memberType) {
if (memberToChangeSignature instanceof PsiMethod) {
final PsiMethod method = MethodSignatureUtil.findMethodBySuperMethod(currentClass, (PsiMethod)memberToChangeSignature, true);
if (method != null && method.getContainingClass() == currentClass) {
myLabeler.addRoot(new TypeMigrationUsageInfo(method), memberType, method, false);
}
} else if (memberToChangeSignature instanceof PsiParameter && ((PsiParameter)memberToChangeSignature).getDeclarationScope() instanceof PsiMethod) {
final PsiMethod superMethod = (PsiMethod)((PsiParameter)memberToChangeSignature).getDeclarationScope();
final int parameterIndex = superMethod.getParameterList().getParameterIndex((PsiParameter)memberToChangeSignature);
final PsiMethod method = MethodSignatureUtil.findMethodBySuperMethod(currentClass, superMethod, true);
if (method != null && method.getContainingClass() == currentClass) {
final PsiParameter parameter = method.getParameterList().getParameters()[parameterIndex];
myLabeler.addRoot(new TypeMigrationUsageInfo(parameter), memberType, parameter, false);
}
}
}
private static class TypeParameterSearcher extends PsiTypeVisitor<Boolean> {
private final Set<PsiTypeParameter> myTypeParams = new HashSet<PsiTypeParameter>();
private TypeParameterSearcher(final PsiTypeParameter[] set) {
ContainerUtil.addAll(myTypeParams, set);
}
public Boolean visitType(final PsiType type) {
return false;
}
public Boolean visitArrayType(final PsiArrayType arrayType) {
return arrayType.getComponentType().accept(this);
}
public Boolean visitClassType(final PsiClassType classType) {
final PsiClass aClass = classType.resolve();
if (aClass instanceof PsiTypeParameter && myTypeParams.contains((PsiTypeParameter)aClass)) return true;
final PsiType[] types = classType.getParameters();
for (final PsiType psiType : types) {
if (psiType.accept(this).booleanValue()) return true;
}
return false;
}
public Boolean visitWildcardType(final PsiWildcardType wildcardType) {
final PsiType bound = wildcardType.getBound();
return bound != null && bound.accept(this).booleanValue();
}
}
}