blob: 2fb305147d02079c2234fea7534fe58cee9f8153 [file] [log] [blame]
/*
* User: anna
* Date: 18-Aug-2009
*/
package com.intellij.refactoring.typeMigration.rules;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.tree.IElementType;
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.NotNull;
import org.jetbrains.annotations.Nullable;
public class ThreadLocalConversionRule extends TypeConversionRule {
private static final Logger LOG = Logger.getInstance("#" + ThreadLocalConversionRule.class.getName());
@Override
public TypeConversionDescriptorBase findConversion(PsiType from,
PsiType to,
PsiMember member,
PsiExpression context,
TypeMigrationLabeler labeler) {
if (to instanceof PsiClassType && isThreadLocalTypeMigration(from, (PsiClassType)to, context)) {
return findDirectConversion(context, to, from, labeler);
}
return null;
}
private static boolean isThreadLocalTypeMigration(PsiType from, PsiClassType to, PsiExpression context) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(to);
final PsiClass threadLocalClass = resolveResult.getElement();
if (threadLocalClass != null) {
final String typeQualifiedName = threadLocalClass.getQualifiedName();
if (!Comparing.strEqual(typeQualifiedName, ThreadLocal.class.getName())) {
return false;
}
final PsiTypeParameter[] typeParameters = threadLocalClass.getTypeParameters();
if (typeParameters.length != 1) return !PsiUtil.isLanguageLevel5OrHigher(context);
final PsiType toTypeParameterValue = resolveResult.getSubstitutor().substitute(typeParameters[0]);
if (toTypeParameterValue != null) {
if (from instanceof PsiPrimitiveType) {
final PsiPrimitiveType unboxedInitialType = PsiPrimitiveType.getUnboxedType(toTypeParameterValue);
if (unboxedInitialType != null) {
return TypeConversionUtil.areTypesConvertible(from, unboxedInitialType);
}
}
else {
return TypeConversionUtil.isAssignable(from, PsiUtil.captureToplevelWildcards(toTypeParameterValue, context));
}
}
return !PsiUtil.isLanguageLevel5OrHigher(context);
}
return false;
}
@Nullable
public static TypeConversionDescriptor findDirectConversion(PsiElement context, PsiType to, PsiType from, TypeMigrationLabeler labeler) {
final PsiClass toTypeClass = PsiUtil.resolveClassInType(to);
LOG.assertTrue(toTypeClass != null);
if (context instanceof PsiArrayAccessExpression) {
return new TypeConversionDescriptor("$qualifier$[$val$]", "$qualifier$.get()[$val$]");
}
final PsiElement parent = context.getParent();
if (parent instanceof PsiAssignmentExpression) {
final IElementType operationSign = ((PsiAssignmentExpression)parent).getOperationTokenType();
if (operationSign == JavaTokenType.EQ) {
return new TypeConversionDescriptor("$qualifier$ = $val$", "$qualifier$.set(" + toBoxed("$val$", from, context)+")", (PsiAssignmentExpression)parent);
}
}
if (context instanceof PsiReferenceExpression) {
final PsiExpression qualifierExpression = ((PsiReferenceExpression)context).getQualifierExpression();
final PsiExpression expression = context.getParent() instanceof PsiMethodCallExpression && qualifierExpression != null
? qualifierExpression
: (PsiExpression)context;
return new TypeConversionDescriptor("$qualifier$", toPrimitive("$qualifier$.get()", from, context), expression);
}
else if (context instanceof PsiBinaryExpression) {
final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)context;
final String sign = binaryExpression.getOperationSign().getText();
return new TypeConversionDescriptor("$qualifier$" + sign + "$val$", toPrimitive("$qualifier$.get()", from, context) + " " + sign + " $val$");
}
if (parent instanceof PsiExpressionStatement) {
if (context instanceof PsiPostfixExpression) {
final PsiPostfixExpression postfixExpression = (PsiPostfixExpression)context;
final String sign = postfixExpression.getOperationSign().getText();
return new TypeConversionDescriptor("$qualifier$" + sign, "$qualifier$.set(" +
getBoxedWrapper(from, to, toPrimitive("$qualifier$.get()", from, context) + " " + sign.charAt(0) + " 1",
labeler, context, postfixExpression.getOperand().getText() +
sign.charAt(0) +
" 1") +
")");
}
else if (context instanceof PsiPrefixExpression) {
final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)context;
final PsiJavaToken operationSign = ((PsiPrefixExpression)context).getOperationSign();
if (operationSign.getTokenType() == JavaTokenType.EXCL) {
return new TypeConversionDescriptor("!$qualifier$", "!$qualifier$.get()");
}
final String sign = operationSign.getText();
final PsiExpression operand = prefixExpression.getOperand();
return new TypeConversionDescriptor(sign + "$qualifier$", "$qualifier$.set(" +
getBoxedWrapper(from, to, toPrimitive("$qualifier$.get()", from, context) + " " + sign.charAt(0) + " 1",
labeler, context, operand != null ? operand.getText() +
sign.charAt(0) +
" 1" : null) +
")");
}
else if (context instanceof PsiAssignmentExpression) {
final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)context;
final PsiJavaToken signToken = assignmentExpression.getOperationSign();
final IElementType operationSign = signToken.getTokenType();
final String sign = signToken.getText();
final PsiExpression lExpression = assignmentExpression.getLExpression();
if (operationSign == JavaTokenType.EQ) {
if (lExpression instanceof PsiReferenceExpression) {
final PsiElement element = ((PsiReferenceExpression)lExpression).resolve();
if (element instanceof PsiVariable && ((PsiVariable)element).hasModifierProperty(PsiModifier.FINAL)) {
return wrapWithNewExpression(to, from, ((PsiAssignmentExpression)context).getRExpression());
}
}
return new TypeConversionDescriptor("$qualifier$ = $val$", "$qualifier$.set(" +
toBoxed("$val$", from, context) +
")");
}
else {
final PsiExpression rExpression = assignmentExpression.getRExpression();
return new TypeConversionDescriptor("$qualifier$" + sign + "$val$", "$qualifier$.set(" +
getBoxedWrapper(from, to, toPrimitive("$qualifier$.get()", from, context) +
" " + sign.charAt(0) +
" $val$", labeler, context,
rExpression != null
? lExpression
.getText() +
sign.charAt(0) +
rExpression.getText()
: null) +
")");
}
}
}
return null;
}
public static TypeConversionDescriptor wrapWithNewExpression(PsiType to, PsiType from, PsiExpression initializer) {
final String boxedTypeName = from instanceof PsiPrimitiveType ? ((PsiPrimitiveType)from).getBoxedTypeName() : from.getCanonicalText();
return new TypeConversionDescriptor("$qualifier$", "new " +
to.getCanonicalText() +
"() {\n" +
"@Override \n" +
"protected " +
boxedTypeName +
" initialValue() {\n" +
" return " +
(PsiUtil.isLanguageLevel5OrHigher(initializer)
? initializer.getText()
: (from instanceof PsiPrimitiveType ? "new " +
((PsiPrimitiveType)from).getBoxedTypeName() +
"(" +
initializer.getText() +
")" : initializer.getText())) +
";\n" +
"}\n" +
"}", initializer);
}
private static String toPrimitive(String replaceByArg, PsiType from, PsiElement context) {
return PsiUtil.isLanguageLevel5OrHigher(context)
? replaceByArg
: from instanceof PsiPrimitiveType ? "((" +
((PsiPrimitiveType)from).getBoxedTypeName() +
")" +
replaceByArg +
")." +
from.getCanonicalText() +
"Value()" : "((" + from.getCanonicalText() + ")" + replaceByArg + ")";
}
private static String toBoxed(String replaceByArg, PsiType from, PsiElement context) {
return PsiUtil.isLanguageLevel5OrHigher(context)
? replaceByArg
: from instanceof PsiPrimitiveType ? "new " + ((PsiPrimitiveType)from).getBoxedTypeName() +
"(" +
replaceByArg +
")"
: replaceByArg;
}
private static String getBoxedWrapper(final PsiType from,
final PsiType to,
@NotNull String arg,
TypeMigrationLabeler labeler,
PsiElement context,
@Nullable String tryType) {
if (from instanceof PsiPrimitiveType) {
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(to);
final PsiClass threadLocalClass = resolveResult.getElement();
LOG.assertTrue(threadLocalClass != null);
final PsiTypeParameter[] typeParameters = threadLocalClass.getTypeParameters();
if (typeParameters.length == 1) {
final PsiType initial = resolveResult.getSubstitutor().substitute(typeParameters[0]);
final PsiPrimitiveType unboxedInitialType = PsiPrimitiveType.getUnboxedType(initial);
if (unboxedInitialType != null) {
LOG.assertTrue(initial != null);
if (tryType != null) {
final PsiType exprType = labeler.getTypeEvaluator().evaluateType(
JavaPsiFacade.getElementFactory(threadLocalClass.getProject()).createExpressionFromText(tryType, context));
if (exprType != null && unboxedInitialType.isAssignableFrom(exprType)) {
return toBoxed(arg, from, context);
}
}
return "new " + initial.getCanonicalText() + "((" + unboxedInitialType.getCanonicalText() + ")(" + arg + "))";
}
}
}
return toBoxed(arg, from, context);
}
}