| /* |
| * 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(); |
| } |
| } |
| |
| } |