package com.intellij.refactoring.typeMigration.rules; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.impl.PsiImplUtil; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase; import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; public class ListArrayConversionRule extends TypeConversionRule { public TypeConversionDescriptorBase findConversion(final PsiType from, final PsiType to, PsiMember member, final PsiExpression context, final TypeMigrationLabeler labeler) { PsiExpression expression = context; PsiClassType classType = from instanceof PsiClassType ? (PsiClassType)from : to instanceof PsiClassType ? (PsiClassType)to : null; PsiArrayType arrayType = from instanceof PsiArrayType ? (PsiArrayType)from : to instanceof PsiArrayType ? (PsiArrayType)to : null; if (classType == null || arrayType == null) return null; final PsiType collectionType = evaluateCollectionsType(classType, expression); if (collectionType == null) return null; if (member == null) { final PsiMethodCallExpression callExpression = PsiTreeUtil.getParentOfType(context, PsiMethodCallExpression.class); if (callExpression != null) { member = callExpression.resolveMethod(); expression = callExpression; } } if (member instanceof PsiMethod) { TypeConversionDescriptor descriptor = changeCollectionCallsToArray((PsiMethod)member, context, collectionType, arrayType); if (descriptor != null) return descriptor; @NonNls final String memberName = member.getName(); assert memberName != null; if (memberName.equals("sort")) { if (((PsiMethod)member).getParameterList().getParametersCount() == 1) { descriptor = new TypeConversionDescriptor("Arrays.sort($qualifier$)", "Collections.sort($qualifier$)", expression); } else { descriptor = new TypeConversionDescriptor("Arrays.sort($qualifier$, $expr$)", "Collections.sort($qualifier$, $expr$)", expression); } } else if (memberName.equals("binarySearch")) { if (((PsiMethod)member).getParameterList().getParametersCount() == 2) { descriptor = new TypeConversionDescriptor("Arrays.binarySearch($qualifier$, $key$)", "Collections.binarySearch($qualifier$, $key$)", expression); } else { descriptor = new TypeConversionDescriptor("Arrays.binarySearch($qualifier$, $key$, $comparator$)", "Collections.binarySearch($qualifier$, $key$, $comparator$)", expression); } } else if (memberName.equals("asList")) { if (((PsiMethod)member).getParameterList().getParametersCount() == 1) { descriptor = new TypeConversionDescriptor("Arrays.asList($qualifier$)", "$qualifier$", expression); } } else if (memberName.equals("fill")) { descriptor = new TypeConversionDescriptor("Arrays.fill($qualifier$, $filler$)", "Collections.fill($qualifier$, $filler$)", expression); } if (descriptor != null) { return from instanceof PsiClassType ? new TypeConversionDescriptor(descriptor.getReplaceByString(), descriptor.getStringToReplace(), descriptor.getExpression()) : descriptor; } } if (member instanceof PsiField && "length".equals(member.getName())) { return new TypeConversionDescriptor("$qualifier$.length", "$qualifier$.size()"); } final PsiElement parent = context.getParent(); if (parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)parent).getLExpression() == context) { if (TypeConversionUtil.isAssignable(collectionType, arrayType.getComponentType())) { return new TypeConversionDescriptor("$qualifier$[$idx$] = $expr$", "$qualifier$.set($idx$, $expr$)", (PsiExpression)parent); } } else if (context instanceof PsiArrayAccessExpression && TypeConversionUtil.isAssignable(arrayType.getComponentType(), collectionType)) { return new TypeConversionDescriptor("$qualifier$[$idx$]", "$qualifier$.get($idx$)"); } return null; } @Nullable public static PsiType evaluateCollectionsType(PsiClassType classType, PsiExpression expression) { final PsiClassType.ClassResolveResult classResolveResult = PsiUtil.resolveGenericsClassInType(classType); final PsiClass psiClass = classResolveResult.getElement(); if (psiClass != null) { final GlobalSearchScope allScope = GlobalSearchScope.allScope(psiClass.getProject()); final PsiClass collectionClass = JavaPsiFacade.getInstance(psiClass.getProject()).findClass(CommonClassNames.JAVA_UTIL_LIST, allScope); if (collectionClass != null && InheritanceUtil.isInheritorOrSelf(psiClass, collectionClass, true)) { final PsiSubstitutor derivedSubstitutor = classResolveResult.getSubstitutor(); if (PsiUtil.isRawSubstitutor(psiClass, derivedSubstitutor)) return null; final PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(collectionClass, psiClass, derivedSubstitutor); assert substitutor != null; final PsiType type = substitutor.substitute(collectionClass.getTypeParameters()[0]); assert type != null; return PsiImplUtil.normalizeWildcardTypeByPosition(type, expression); } } return null; } @Override public Pair<PsiType, PsiType> bindTypeParameters(final PsiType from, final PsiType to, final PsiMethod method, final PsiExpression context, final TypeMigrationLabeler labeler) { if (findConversion(from, to, method, context, labeler) == null) return null; if (from instanceof PsiArrayType && to instanceof PsiClassType) { final PsiType collectionType = evaluateCollectionsType((PsiClassType)to, context); if (collectionType != null) { return Pair.create(((PsiArrayType)from).getComponentType(), collectionType); } } if (to instanceof PsiArrayType && from instanceof PsiClassType) { final PsiType collectionType = evaluateCollectionsType((PsiClassType)from, context); if (collectionType != null) { return Pair.create(collectionType, ((PsiArrayType)to).getComponentType()); } } return null; } @Nullable private static TypeConversionDescriptor changeCollectionCallsToArray(final PsiMethod method, final PsiElement context, PsiType collectionType, PsiArrayType arrayType) { @NonNls final String methodName = method.getName(); if (methodName.equals("toArray")) { if (method.getParameterList().getParameters().length == 0) { return new TypeConversionDescriptor("$qualifier$.toArray()", "$qualifier$"); } return new TypeConversionDescriptor("$qualifier$.toArray($expr$)", "$qualifier$"); } else if (methodName.equals("size")) { return new TypeConversionDescriptor("$qualifier$.size()", "$qualifier$.length"); } else if (methodName.equals("get")) { if (TypeConversionUtil.isAssignable(collectionType, arrayType.getComponentType())) { return new TypeConversionDescriptor("$qualifier$.get($i$)", "$qualifier$[$i$]", PsiTreeUtil.getParentOfType(context, PsiMethodCallExpression.class)); } } else if (methodName.equals("set")) { if (TypeConversionUtil.isAssignable(arrayType.getComponentType(), collectionType)) { return new TypeConversionDescriptor("$qualifier$.set($i$, $val$)", "$qualifier$[$i$] = $val$"); } } return null; } }