diff options
Diffstat (limited to 'java/typeMigration/src/com/intellij/refactoring')
10 files changed, 1549 insertions, 0 deletions
diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeConversionDescriptor.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeConversionDescriptor.java new file mode 100644 index 000000000000..d116c3ce30fe --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeConversionDescriptor.java @@ -0,0 +1,101 @@ +package com.intellij.refactoring.typeMigration; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiExpression; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.structuralsearch.MatchOptions; +import com.intellij.structuralsearch.plugin.replace.ReplaceOptions; +import com.intellij.structuralsearch.plugin.replace.impl.Replacer; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NonNls; + +/** + * Created by IntelliJ IDEA. + * User: db + * Date: Sep 28, 2004 + * Time: 7:13:53 PM + * To change this template use File | Settings | File Templates. + */ +public class TypeConversionDescriptor extends TypeConversionDescriptorBase { + private static final Logger LOG = Logger.getInstance("#" + TypeConversionDescriptor.class.getName()); + + private String myStringToReplace = null; + private String myReplaceByString = "$"; + private PsiExpression myExpression; + + public TypeConversionDescriptor(@NonNls final String stringToReplace, @NonNls final String replaceByString) { + myStringToReplace = stringToReplace; + myReplaceByString = replaceByString; + } + + public TypeConversionDescriptor(@NonNls final String stringToReplace, @NonNls final String replaceByString, final PsiExpression expression) { + myStringToReplace = stringToReplace; + myReplaceByString = replaceByString; + myExpression = expression; + } + + public void setStringToReplace(String stringToReplace) { + myStringToReplace = stringToReplace; + } + + public void setReplaceByString(String replaceByString) { + myReplaceByString = replaceByString; + } + + public String getStringToReplace() { + return myStringToReplace; + } + + public String getReplaceByString() { + return myReplaceByString; + } + + public PsiExpression getExpression() { + return myExpression; + } + + public void setExpression(final PsiExpression expression) { + myExpression = expression; + } + + @Override + public void replace(PsiExpression expression) { + if (getExpression() != null) expression = getExpression(); + final Project project = expression.getProject(); + final ReplaceOptions options = new ReplaceOptions(); + options.setMatchOptions(new MatchOptions()); + final Replacer replacer = new Replacer(project, null); + try { + final String replacement = replacer.testReplace(expression.getText(), getStringToReplace(), getReplaceByString(), options); + try { + JavaCodeStyleManager.getInstance(project).shortenClassReferences(expression.replace( + JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(replacement, expression))); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(); + if (myReplaceByString != null) { + buf.append(myReplaceByString); + } + if (myStringToReplace != null) { + if (buf.length() > 0) buf.append(" "); + buf.append(myStringToReplace); + } + if (myExpression != null) { + if (buf.length() > 0) buf.append(" "); + buf.append(myExpression.getText()); + } + return buf.toString(); + } +} diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeMigrationVariableTypeFixProvider.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeMigrationVariableTypeFixProvider.java new file mode 100644 index 000000000000..d84320e4cd08 --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeMigrationVariableTypeFixProvider.java @@ -0,0 +1,61 @@ +/* + * User: anna + * Date: 27-Aug-2009 + */ +package com.intellij.refactoring.typeMigration; + +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInsight.daemon.impl.quickfix.VariableTypeFix; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.quickfix.ChangeVariableTypeQuickFixProvider; +import com.intellij.openapi.command.undo.UndoUtil; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiType; +import com.intellij.psi.PsiVariable; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class TypeMigrationVariableTypeFixProvider implements ChangeVariableTypeQuickFixProvider { + private static final Logger LOG1 = Logger.getInstance("#" + TypeMigrationVariableTypeFixProvider.class.getName()); + + @NotNull + public IntentionAction[] getFixes(@NotNull PsiVariable variable, @NotNull PsiType toReturn) { + return new IntentionAction[]{new VariableTypeFix(variable, toReturn) { + @NotNull + @Override + public String getText() { + return "Migrate \'" + myName + "\' type to \'" + getReturnType().getCanonicalText() + "\'"; + } + + @Override + public void invoke(@NotNull Project project, + @NotNull PsiFile file, + @Nullable("is null when called from inspection") Editor editor, + @NotNull PsiElement startElement, + @NotNull PsiElement endElement) { + final PsiVariable myVariable = (PsiVariable)startElement; + + if (!FileModificationService.getInstance().prepareFileForWrite(myVariable.getContainingFile())) return; + try { + myVariable.normalizeDeclaration(); + final TypeMigrationRules rules = new TypeMigrationRules(TypeMigrationLabeler.getElementType(myVariable)); + rules.setMigrationRootType(getReturnType()); + rules.setBoundScope(GlobalSearchScope.projectScope(project)); + TypeMigrationProcessor.runHighlightingTypeMigration(project, editor, rules, myVariable); + JavaCodeStyleManager.getInstance(project).shortenClassReferences(myVariable); + UndoUtil.markPsiFileForUndo(file); + } + catch (IncorrectOperationException e) { + LOG1.error(e); + } + } + }}; + } +} diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ChangeClassParametersIntention.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ChangeClassParametersIntention.java new file mode 100644 index 000000000000..9571807cd229 --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ChangeClassParametersIntention.java @@ -0,0 +1,124 @@ +package com.intellij.refactoring.typeMigration.intentions; + +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInsight.hint.HintManager; +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.codeInsight.intention.impl.TypeExpression; +import com.intellij.codeInsight.template.*; +import com.intellij.codeInsight.template.impl.TemplateState; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.typeMigration.TypeMigrationProcessor; +import com.intellij.refactoring.typeMigration.TypeMigrationRules; +import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; + +/** + * @author anna + */ +public class ChangeClassParametersIntention extends PsiElementBaseIntentionAction { + + private static final Logger LOG = Logger.getInstance("#" + ChangeClassParametersIntention.class); + + @NotNull + @Override + public String getText() { + return getFamilyName(); + } + + @NotNull + @Override + public String getFamilyName() { + return "Change class type parameter"; + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { + final PsiTypeElement typeElement = PsiTreeUtil.getTopmostParentOfType(element, PsiTypeElement.class); + final PsiElement parent = typeElement != null ? typeElement.getParent() : null; + final PsiReferenceParameterList parameterList = parent instanceof PsiReferenceParameterList ? (PsiReferenceParameterList)parent : null; + if (parameterList != null && parameterList.getTypeArguments().length > 0) { + final PsiMember member = PsiTreeUtil.getParentOfType(parameterList, PsiMember.class); + if (member instanceof PsiAnonymousClass) { + final PsiClassType.ClassResolveResult result = ((PsiAnonymousClass)member).getBaseClassType().resolveGenerics(); + return result.getElement() != null && ((PsiAnonymousClass)member).getBaseClassReference().getParameterList() == parameterList; + } + } + return false; + } + + @Override + public void invoke(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) throws IncorrectOperationException { + if (!FileModificationService.getInstance().preparePsiElementsForWrite(element)) return; + + final PsiTypeElement typeElement = PsiTreeUtil.getTopmostParentOfType(element, PsiTypeElement.class); + final PsiReferenceParameterList parameterList = PsiTreeUtil.getParentOfType(element, PsiReferenceParameterList.class); + if (parameterList != null && typeElement != null) { + final PsiClass aClass = PsiTreeUtil.getParentOfType(element, PsiClass.class); + if (aClass instanceof PsiAnonymousClass) { + editor.getCaretModel().moveToOffset(aClass.getTextOffset()); + final PsiTypeElement[] typeElements = parameterList.getTypeParameterElements(); + final int changeIdx = ArrayUtil.find(typeElements, typeElement); + + final PsiClassType.ClassResolveResult result = ((PsiAnonymousClass)aClass).getBaseClassType().resolveGenerics(); + final PsiClass baseClass = result.getElement(); + LOG.assertTrue(baseClass != null); + final PsiTypeParameter typeParameter = baseClass.getTypeParameters()[changeIdx]; + + final TemplateBuilderImpl templateBuilder = (TemplateBuilderImpl)TemplateBuilderFactory.getInstance().createTemplateBuilder(aClass); + + final String oldTypeText = typeElement.getText(); + final String varName = "param"; + templateBuilder.replaceElement(typeElement, varName, new TypeExpression(project, new PsiType[]{typeElement.getType()}), true); + + final Template template = templateBuilder.buildInlineTemplate(); + TemplateManager.getInstance(project).startTemplate(editor, template, false, null, new TemplateEditingAdapter() { + private String myNewType; + + @Override + public void beforeTemplateFinished(TemplateState state, Template template) { + final TextResult value = state.getVariableValue(varName); + myNewType = value != null ? value.getText() : ""; + final int segmentsCount = state.getSegmentsCount(); + final Document document = state.getEditor().getDocument(); + for (int i = 0; i < segmentsCount; i++) { + final TextRange segmentRange = state.getSegmentRange(i); + document.replaceString(segmentRange.getStartOffset(), segmentRange.getEndOffset(), oldTypeText); + } + } + + @Override + public void templateFinished(Template template, boolean brokenOff) { + if (!brokenOff) { + final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); + try { + final PsiType targetParam = elementFactory.createTypeFromText(myNewType, aClass); + final TypeMigrationRules myRules = new TypeMigrationRules(((PsiAnonymousClass)aClass).getBaseClassType()); + final PsiSubstitutor substitutor = result.getSubstitutor().put(typeParameter, targetParam); + final PsiType targetClassType = elementFactory.createType(baseClass, substitutor); + myRules.setMigrationRootType(targetClassType); + myRules.setBoundScope(new LocalSearchScope(aClass)); + new TypeMigrationProcessor(project, ((PsiAnonymousClass)aClass).getBaseClassReference().getParameterList(), myRules).run(); + } + catch (IncorrectOperationException e) { + HintManager.getInstance().showErrorHint(editor, "Incorrect type"); + } + } + } + }); + } + } + } + + @Override + public boolean startInWriteAction() { + return true; + } +} diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java new file mode 100644 index 000000000000..d794d59f9943 --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java @@ -0,0 +1,218 @@ +package com.intellij.refactoring.typeMigration.intentions; + +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInsight.intention.LowPriorityAction; +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.impl.AllowedApiFilterExtension; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; +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.TypeMigrationReplacementUtil; +import com.intellij.refactoring.typeMigration.rules.AtomicConversionRule; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.Query; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.*; + +import static com.intellij.psi.util.TypeConversionUtil.isBinaryOperatorApplicable; +import static com.intellij.util.ObjectUtils.assertNotNull; + +/** + * @author anna + * @since 26-Aug-2009 + */ +public class ConvertFieldToAtomicIntention extends PsiElementBaseIntentionAction implements LowPriorityAction { + private static final Logger LOG = Logger.getInstance("#" + ConvertFieldToAtomicIntention.class.getName()); + + private final Map<PsiType, String> myFromToMap = ContainerUtil.newHashMap(); + { + myFromToMap.put(PsiType.INT, AtomicInteger.class.getName()); + myFromToMap.put(PsiType.LONG, AtomicLong.class.getName()); + myFromToMap.put(PsiType.BOOLEAN, AtomicBoolean.class.getName()); + myFromToMap.put(PsiType.INT.createArrayType(), AtomicIntegerArray.class.getName()); + myFromToMap.put(PsiType.LONG.createArrayType(), AtomicLongArray.class.getName()); + } + + @NotNull + @Override + public String getText() { + return "Convert to atomic"; + } + + @NotNull + @Override + public String getFamilyName() { + return getText(); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { + PsiVariable psiVariable = getVariable(element); + if (psiVariable == null || psiVariable instanceof PsiResourceVariable) return false; + if (psiVariable.getLanguage() != JavaLanguage.INSTANCE) return false; + if (psiVariable.getTypeElement() == null) return false; + if (!PsiUtil.isLanguageLevel5OrHigher(psiVariable)) return false; + final PsiType psiType = psiVariable.getType(); + final PsiClass psiTypeClass = PsiUtil.resolveClassInType(psiType); + if (psiTypeClass != null) { + final String qualifiedName = psiTypeClass.getQualifiedName(); + if (qualifiedName != null) { //is already atomic + if (myFromToMap.values().contains(qualifiedName) || + qualifiedName.equals(AtomicReference.class.getName()) || + qualifiedName.equals(AtomicReferenceArray.class.getName())) { + return false; + } + } + } + else if (!myFromToMap.containsKey(psiType)) { + return false; + } + return AllowedApiFilterExtension.isClassAllowed(AtomicReference.class.getName(), element); + } + + private static PsiVariable getVariable(PsiElement element) { + if (element instanceof PsiIdentifier) { + final PsiElement parent = element.getParent(); + if (parent instanceof PsiLocalVariable || parent instanceof PsiField) { + return (PsiVariable)parent; + } + } + return null; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { + final PsiVariable psiVariable = getVariable(element); + LOG.assertTrue(psiVariable != null); + + final Query<PsiReference> refs = ReferencesSearch.search(psiVariable); + + final Set<PsiElement> elements = new HashSet<PsiElement>(); + elements.add(element); + for (PsiReference reference : refs) { + elements.add(reference.getElement()); + } + if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) return; + + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiType fromType = psiVariable.getType(); + PsiClassType toType; + final String atomicQualifiedName = myFromToMap.get(fromType); + if (atomicQualifiedName != null) { + final PsiClass atomicClass = psiFacade.findClass(atomicQualifiedName, GlobalSearchScope.allScope(project)); + if (atomicClass == null) {//show warning + return; + } + toType = factory.createType(atomicClass); + } + else if (fromType instanceof PsiArrayType) { + final PsiClass atomicReferenceArrayClass = + psiFacade.findClass(AtomicReferenceArray.class.getName(), GlobalSearchScope.allScope(project)); + if (atomicReferenceArrayClass == null) {//show warning + return; + } + final Map<PsiTypeParameter, PsiType> substitutor = ContainerUtil.newHashMap(); + final PsiTypeParameter[] typeParameters = atomicReferenceArrayClass.getTypeParameters(); + if (typeParameters.length == 1) { + PsiType componentType = ((PsiArrayType)fromType).getComponentType(); + if (componentType instanceof PsiPrimitiveType) componentType = ((PsiPrimitiveType)componentType).getBoxedType(element); + substitutor.put(typeParameters[0], componentType); + } + toType = factory.createType(atomicReferenceArrayClass, factory.createSubstitutor(substitutor)); + } + else { + final PsiClass atomicReferenceClass = psiFacade.findClass(AtomicReference.class.getName(), GlobalSearchScope.allScope(project)); + if (atomicReferenceClass == null) {//show warning + return; + } + final Map<PsiTypeParameter, PsiType> substitutor = ContainerUtil.newHashMap(); + final PsiTypeParameter[] typeParameters = atomicReferenceClass.getTypeParameters(); + if (typeParameters.length == 1) { + PsiType type = fromType; + if (type instanceof PsiPrimitiveType) type = ((PsiPrimitiveType)fromType).getBoxedType(element); + substitutor.put(typeParameters[0], type); + } + toType = factory.createType(atomicReferenceClass, factory.createSubstitutor(substitutor)); + } + + try { + for (PsiReference reference : refs) { + PsiElement psiElement = reference.getElement(); + if (psiElement instanceof PsiExpression) { + final PsiElement parent = psiElement.getParent(); + if (parent instanceof PsiExpression && !(parent instanceof PsiReferenceExpression || parent instanceof PsiPolyadicExpression)) { + psiElement = parent; + } + if (psiElement instanceof PsiBinaryExpression) { + PsiBinaryExpression binary = (PsiBinaryExpression)psiElement; + if (isBinaryOperatorApplicable(binary.getOperationTokenType(), binary.getLOperand(), binary.getROperand(), true)) { + continue; + } + } + else if (psiElement instanceof PsiAssignmentExpression) { + final PsiAssignmentExpression assignment = (PsiAssignmentExpression)psiElement; + final IElementType opSign = TypeConversionUtil.convertEQtoOperation(assignment.getOperationTokenType()); + if (opSign != null && isBinaryOperatorApplicable(opSign, assignment.getLExpression(), assignment.getRExpression(), true)) { + continue; + } + } + final TypeConversionDescriptor directConversion = AtomicConversionRule.findDirectConversion(psiElement, toType, fromType); + if (directConversion != null) { + TypeMigrationReplacementUtil.replaceExpression((PsiExpression)psiElement, project, directConversion); + } + } + } + + final PsiExpression initializer = psiVariable.getInitializer(); + if (initializer != null) { + final TypeConversionDescriptor directConversion = AtomicConversionRule.wrapWithNewExpression(toType, fromType, null, element); + if (directConversion != null) { + TypeMigrationReplacementUtil.replaceExpression(initializer, project, directConversion); + } + } + else if (!assertNotNull(psiVariable.getModifierList()).hasModifierProperty(PsiModifier.FINAL)) { + final PsiExpression newInitializer = factory.createExpressionFromText("new " + toType.getCanonicalText() + "()", psiVariable); + if (psiVariable instanceof PsiLocalVariable) { + ((PsiLocalVariable)psiVariable).setInitializer(newInitializer); + } + else if (psiVariable instanceof PsiField) { + ((PsiField)psiVariable).setInitializer(newInitializer); + } + JavaCodeStyleManager.getInstance(project).shortenClassReferences(psiVariable.getInitializer()); + } + + PsiElement replaced = assertNotNull(psiVariable.getTypeElement()).replace(factory.createTypeElement(toType)); + JavaCodeStyleManager.getInstance(project).shortenClassReferences(replaced); + + if (psiVariable instanceof PsiField || CodeStyleSettingsManager.getSettings(project).GENERATE_FINAL_LOCALS) { + final PsiModifierList modifierList = assertNotNull(psiVariable.getModifierList()); + modifierList.setModifierProperty(PsiModifier.FINAL, true); + modifierList.setModifierProperty(PsiModifier.VOLATILE, false); + } + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + + @Override + public boolean startInWriteAction() { + return true; + } +} diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java new file mode 100644 index 000000000000..e01aaadae7d8 --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java @@ -0,0 +1,145 @@ +/* + * User: anna + * Date: 26-Aug-2009 + */ +package com.intellij.refactoring.typeMigration.intentions; + +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInsight.intention.LowPriorityAction; +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.impl.AllowedApiFilterExtension; +import com.intellij.psi.impl.PsiDiamondTypeUtil; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; +import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; +import com.intellij.refactoring.typeMigration.TypeMigrationReplacementUtil; +import com.intellij.refactoring.typeMigration.TypeMigrationRules; +import com.intellij.refactoring.typeMigration.rules.ThreadLocalConversionRule; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.Query; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static com.intellij.util.ObjectUtils.assertNotNull; + +public class ConvertFieldToThreadLocalIntention extends PsiElementBaseIntentionAction implements LowPriorityAction { + private static final Logger LOG = Logger.getInstance("#" + ConvertFieldToThreadLocalIntention.class.getName()); + + @NotNull + @Override + public String getText() { + return "Convert to ThreadLocal"; + } + + @NotNull + @Override + public String getFamilyName() { + return getText(); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { + if (!(element instanceof PsiIdentifier)) return false; + final PsiField psiField = PsiTreeUtil.getParentOfType(element, PsiField.class); + if (psiField == null) return false; + if (psiField.getLanguage() != JavaLanguage.INSTANCE) return false; + if (psiField.getTypeElement() == null) return false; + final PsiType fieldType = psiField.getType(); + final PsiClass fieldTypeClass = PsiUtil.resolveClassInType(fieldType); + if (fieldType instanceof PsiPrimitiveType || fieldType instanceof PsiArrayType) return true; + return fieldTypeClass != null && !Comparing.strEqual(fieldTypeClass.getQualifiedName(), ThreadLocal.class.getName()) + && AllowedApiFilterExtension.isClassAllowed(ThreadLocal.class.getName(), element); + } + + @Override + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { + final PsiField psiField = PsiTreeUtil.getParentOfType(element, PsiField.class); + LOG.assertTrue(psiField != null); + final Query<PsiReference> refs = ReferencesSearch.search(psiField); + + final Set<PsiElement> elements = new HashSet<PsiElement>(); + elements.add(element); + for (PsiReference reference : refs) { + elements.add(reference.getElement()); + } + if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) return; + + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiType fromType = psiField.getType(); + + final PsiClass threadLocalClass = psiFacade.findClass(ThreadLocal.class.getName(), GlobalSearchScope.allScope(project)); + if (threadLocalClass == null) {//show warning + return; + } + final Map<PsiTypeParameter, PsiType> substitutor = ContainerUtil.newHashMap(); + final PsiTypeParameter[] typeParameters = threadLocalClass.getTypeParameters(); + if (typeParameters.length == 1) { + PsiType type = fromType; + if (fromType instanceof PsiPrimitiveType) type = ((PsiPrimitiveType)fromType).getBoxedType(element); + substitutor.put(typeParameters[0], type); + } + final PsiClassType toType = factory.createType(threadLocalClass, factory.createSubstitutor(substitutor)); + + try { + final TypeMigrationRules rules = new TypeMigrationRules(fromType); + rules.setMigrationRootType(toType); + rules.setBoundScope(GlobalSearchScope.fileScope(element.getContainingFile())); + final TypeMigrationLabeler labeler = new TypeMigrationLabeler(rules); + labeler.getMigratedUsages(false, psiField); + for (PsiReference reference : refs) { + PsiElement psiElement = reference.getElement(); + if (psiElement instanceof PsiExpression) { + final PsiElement parent = psiElement.getParent(); + if (parent instanceof PsiExpression && !(parent instanceof PsiReferenceExpression || parent instanceof PsiPolyadicExpression)) { + psiElement = parent; + } + final TypeConversionDescriptor conversion = ThreadLocalConversionRule.findDirectConversion(psiElement, toType, fromType, labeler); + if (conversion != null) { + TypeMigrationReplacementUtil.replaceExpression((PsiExpression)psiElement, project, conversion); + } + } + } + + final PsiExpression initializer = psiField.getInitializer(); + if (initializer != null) { + final TypeConversionDescriptor conversion = ThreadLocalConversionRule.wrapWithNewExpression(toType, fromType, initializer); + TypeMigrationReplacementUtil.replaceExpression(initializer, project, conversion); + CodeStyleManager.getInstance(project).reformat(psiField); + } + else if (!assertNotNull(psiField.getModifierList()).hasModifierProperty(PsiModifier.FINAL)) { + final String text = "new " + PsiDiamondTypeUtil.getCollapsedType(toType, psiField) + "()"; + final PsiExpression newInitializer = factory.createExpressionFromText(text, psiField); + psiField.setInitializer(newInitializer); + } + + assertNotNull(psiField.getTypeElement()).replace(factory.createTypeElement(toType)); + + final PsiModifierList modifierList = assertNotNull(psiField.getModifierList()); + modifierList.setModifierProperty(PsiModifier.FINAL, true); + modifierList.setModifierProperty(PsiModifier.VOLATILE, false); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + + @Override + public boolean startInWriteAction() { + return true; + } +} diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java new file mode 100644 index 000000000000..a4ec46e44838 --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java @@ -0,0 +1,420 @@ +/* + * 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; + +import java.util.concurrent.atomic.*; + +public class AtomicConversionRule extends TypeConversionRule { + private static final Logger LOG = Logger.getInstance("#" + AtomicConversionRule.class.getName()); + + + @Override + public TypeConversionDescriptorBase findConversion(PsiType from, + PsiType to, + PsiMember member, + PsiExpression context, + TypeMigrationLabeler labeler) { + if (to instanceof PsiClassType && isAtomicTypeMigration(from, (PsiClassType)to, context)) { + return findDirectConversion(context, to, from); + } + else if (from instanceof PsiClassType && isAtomicTypeMigration(to, (PsiClassType)from, context)) { + return findReverseConversion(context); + } + return null; + } + + private static boolean isAtomicTypeMigration(PsiType from, PsiClassType to, PsiExpression context) { + if (from == PsiType.INT && to.getCanonicalText().equals(AtomicInteger.class.getName())) { + return true; + } + if (from.equals(PsiType.INT.createArrayType()) && to.getCanonicalText().equals(AtomicIntegerArray.class.getName())) { + return true; + } + if (from == PsiType.LONG && to.getCanonicalText().equals(AtomicLong.class.getName())) { + return true; + } + if (from.equals(PsiType.LONG.createArrayType()) && to.getCanonicalText().equals(AtomicLongArray.class.getName())) { + return true; + } + if (from == PsiType.BOOLEAN && to.getCanonicalText().equals(AtomicBoolean.class.getName())) { + return true; + } + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(to); + final PsiClass atomicClass = resolveResult.getElement(); + + if (atomicClass != null) { + final String typeQualifiedName = atomicClass.getQualifiedName(); + if (!Comparing.strEqual(typeQualifiedName, AtomicReference.class.getName()) && + !Comparing.strEqual(typeQualifiedName, AtomicReferenceArray.class.getName())) { + return false; + } + final PsiTypeParameter[] typeParameters = atomicClass.getTypeParameters(); + if (typeParameters.length != 1) return false; + final PsiType toTypeParameterValue = resolveResult.getSubstitutor().substitute(typeParameters[0]); + if (toTypeParameterValue != null) { + if (from.getDeepComponentType() instanceof PsiPrimitiveType) { + final PsiPrimitiveType unboxedInitialType = PsiPrimitiveType.getUnboxedType(toTypeParameterValue); + if (unboxedInitialType != null) { + return TypeConversionUtil.areTypesConvertible(from.getDeepComponentType(), unboxedInitialType); + } + } + else { + return TypeConversionUtil.isAssignable(from.getDeepComponentType(), PsiUtil.captureToplevelWildcards(toTypeParameterValue, context)); + } + } + } + return false; + } + + @Nullable + public static TypeConversionDescriptor findDirectConversion(PsiElement context, PsiType to, PsiType from) { + final PsiClass toTypeClass = PsiUtil.resolveClassInType(to); + LOG.assertTrue(toTypeClass != null); + final String qualifiedName = toTypeClass.getQualifiedName(); + if (qualifiedName != null) { + if (qualifiedName.equals(AtomicInteger.class.getName()) || qualifiedName.equals(AtomicLong.class.getName())) { + + if (context instanceof PsiPostfixExpression) { + final IElementType operationSign = ((PsiPostfixExpression)context).getOperationTokenType(); + if (operationSign == JavaTokenType.MINUSMINUS) { + return new TypeConversionDescriptor("$qualifier$--", "$qualifier$.getAndDecrement()"); + } + if (operationSign == JavaTokenType.PLUSPLUS) { + return new TypeConversionDescriptor("$qualifier$++", "$qualifier$.getAndIncrement()"); + } + + } + else if (context instanceof PsiPrefixExpression) { + final IElementType operationSign = ((PsiPrefixExpression)context).getOperationTokenType(); + if (operationSign == JavaTokenType.MINUSMINUS) { + return new TypeConversionDescriptor("--$qualifier$", "$qualifier$.decrementAndGet()"); + } + if (operationSign == JavaTokenType.PLUSPLUS) { + return new TypeConversionDescriptor("++$qualifier$", "$qualifier$.incrementAndGet()"); + } + + } + else if (context instanceof PsiAssignmentExpression) { + final PsiJavaToken signToken = ((PsiAssignmentExpression)context).getOperationSign(); + final IElementType operationSign = signToken.getTokenType(); + final String sign = signToken.getText(); + if (operationSign == JavaTokenType.PLUSEQ || operationSign == JavaTokenType.MINUSEQ) { + return new TypeConversionDescriptor("$qualifier$ " + sign + " $val$", "$qualifier$.getAndAdd(" + + (operationSign == JavaTokenType.MINUSEQ ? "-" : "") + + "($val$))"); + } + } + } + else if (qualifiedName.equals(AtomicIntegerArray.class.getName()) || qualifiedName.equals(AtomicLongArray.class.getName())) { + PsiElement parentExpression = context.getParent(); + if (parentExpression instanceof PsiPostfixExpression) { + final IElementType operationSign = ((PsiPostfixExpression)parentExpression).getOperationTokenType(); + if (operationSign == JavaTokenType.MINUSMINUS) { + return new TypeConversionDescriptor("$qualifier$[$idx$]--", "$qualifier$.getAndDecrement($idx$)", + (PsiExpression)parentExpression); + } + if (operationSign == JavaTokenType.PLUSPLUS) { + return new TypeConversionDescriptor("$qualifier$[$idx$]++", "$qualifier$.getAndIncrement($idx$)", + (PsiExpression)parentExpression); + } + + } + else if (parentExpression instanceof PsiPrefixExpression) { + final IElementType operationSign = ((PsiPrefixExpression)parentExpression).getOperationTokenType(); + if (operationSign == JavaTokenType.MINUSMINUS) { + return new TypeConversionDescriptor("--$qualifier$[$idx$]", "$qualifier$.decrementAndGet($idx$)", + (PsiExpression)parentExpression); + } + if (operationSign == JavaTokenType.PLUSPLUS) { + return new TypeConversionDescriptor("++$qualifier$[$idx$]", "$qualifier$.incrementAndGet($idx$)", + (PsiExpression)parentExpression); + } + + } + else if (parentExpression instanceof PsiAssignmentExpression) { + final PsiJavaToken signToken = ((PsiAssignmentExpression)parentExpression).getOperationSign(); + final IElementType operationSign = signToken.getTokenType(); + final String sign = signToken.getText(); + if (operationSign == JavaTokenType.PLUSEQ || operationSign == JavaTokenType.MINUSEQ) { + return new TypeConversionDescriptor("$qualifier$[$idx$] " + sign + " $val$", "$qualifier$.getAndAdd($idx$, " + + (operationSign == JavaTokenType.MINUSEQ + ? "-" + : "") + + "($val$))", (PsiExpression)parentExpression); + } + } + } + } + return from instanceof PsiArrayType + ? findDirectConversionForAtomicReferenceArray(context, to, from) + : findDirectConversionForAtomicReference(context, to, from); + } + + @Nullable + private static TypeConversionDescriptor findDirectConversionForAtomicReference(PsiElement context, PsiType to, PsiType from) { + 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($val$)", (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$", "$qualifier$.get()", expression); + } + else if (context instanceof PsiAssignmentExpression) { + final PsiJavaToken signToken = ((PsiAssignmentExpression)context).getOperationSign(); + final IElementType operationSign = signToken.getTokenType(); + final String sign = signToken.getText(); + if (parent instanceof PsiExpressionStatement) { + if (operationSign == JavaTokenType.EQ) { + final PsiExpression lExpression = ((PsiAssignmentExpression)context).getLExpression(); + 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(), element); + } + } + return new TypeConversionDescriptor("$qualifier$ = $val$", "$qualifier$.set($val$)"); + } + else { + return new TypeConversionDescriptor("$qualifier$" + sign + "$val$", "$qualifier$.set(" + + getBoxedWrapper(from, to, "$qualifier$.get() " + + sign.charAt(0) + + " $val$") + + ")"); + } + } //else should be a conflict + } + else if (context instanceof PsiPostfixExpression) { + final String sign = ((PsiPostfixExpression)context).getOperationSign().getText(); + return new TypeConversionDescriptor("$qualifier$" + sign, "$qualifier$.getAndSet(" + + getBoxedWrapper(from, to, "$qualifier$.get() " + sign.charAt(0) + " 1") + + ")"); + } + else if (context instanceof PsiPrefixExpression) { + final PsiJavaToken operationSign = ((PsiPrefixExpression)context).getOperationSign(); + if (operationSign.getTokenType() == JavaTokenType.EXCL) { + return new TypeConversionDescriptor("!$qualifier$", "!$qualifier$.get()"); + } + final String sign = operationSign.getText(); + return new TypeConversionDescriptor(sign + "$qualifier$", "$qualifier$.set(" + //todo reject? + getBoxedWrapper(from, to, "$qualifier$.get() " + sign.charAt(0) + " 1") + + ")"); + } else if (context instanceof PsiBinaryExpression) { + final String sign = ((PsiBinaryExpression)context).getOperationSign().getText(); + return new TypeConversionDescriptor("$qualifier$" + sign + "$val$", "$qualifier$.get() " + sign + " $val$"); + } + + if (parent instanceof PsiVariable) { + return wrapWithNewExpression(to, from, null, parent); + } + return null; + } + + public static TypeConversionDescriptor wrapWithNewExpression(PsiType to, PsiType from, @Nullable PsiExpression expression, PsiElement context) { + final String typeText = PsiDiamondTypeUtil.getCollapsedType(to, context); + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(to); + final PsiClass atomicClass = resolveResult.getElement(); + LOG.assertTrue(atomicClass != null); + final PsiTypeParameter[] typeParameters = atomicClass.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 (from instanceof PsiPrimitiveType) { + final PsiClassType boxedFromType = ((PsiPrimitiveType)from).getBoxedType(atomicClass); + LOG.assertTrue(boxedFromType != null); + if (!TypeConversionUtil.isAssignable(initial, boxedFromType)) { + return new TypeConversionDescriptor("$val$", "new " + typeText + "((" + unboxedInitialType.getCanonicalText() + ")$val$)", expression); + } + } + } + } + return new TypeConversionDescriptor("$val$", "new " + typeText + "($val$)", expression); + } + + @Nullable + private static TypeConversionDescriptor findDirectConversionForAtomicReferenceArray(PsiElement context, PsiType to, PsiType from) { + LOG.assertTrue(from instanceof PsiArrayType); + from = ((PsiArrayType)from).getComponentType(); + final PsiElement parent = context.getParent(); + final PsiElement parentParent = parent.getParent(); + + if (parent instanceof PsiAssignmentExpression) { + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent; + final IElementType operationSign = assignmentExpression.getOperationTokenType(); + final String sign = assignmentExpression.getOperationSign().getText(); + if (context instanceof PsiArrayAccessExpression) { + if (parentParent instanceof PsiExpressionStatement) { + if (assignmentExpression.getLExpression() == context) { + if (operationSign == JavaTokenType.EQ) { + return new TypeConversionDescriptor("$qualifier$[$idx$] = $val$", "$qualifier$.set($idx$, $val$)", assignmentExpression); + } + else { + return new TypeConversionDescriptor("$qualifier$[$idx$]" + sign + "$val$", + "$qualifier$.set($idx$, " + getBoxedWrapper(from, to, "$qualifier$.get($idx$) " + sign.charAt(0) + " $val$") + ")", + assignmentExpression); + } + } + } //else should be a conflict + } + else { + final PsiExpression rExpression = assignmentExpression.getRExpression(); + if (rExpression == context && operationSign == JavaTokenType.EQ) { //array = new T[l]; + return wrapWithNewExpression(to, from, rExpression, context); + } + } + } else if (parent instanceof PsiVariable) { + if (((PsiVariable)parent).getInitializer() == context) { + return wrapWithNewExpression(to, from, (PsiExpression)context, context); + } + } + + if (parentParent instanceof PsiExpressionStatement) { + if (parent instanceof PsiPostfixExpression) { + final String sign = ((PsiPostfixExpression)parent).getOperationSign().getText(); + return new TypeConversionDescriptor("$qualifier$[$idx$]" + sign, "$qualifier$.getAndSet($idx$, " + + getBoxedWrapper(from, to, + "$qualifier$.get($idx$) " + sign.charAt(0) + " 1") + + ")", (PsiExpression)parent); + } + else if (parent instanceof PsiPrefixExpression) { + final String sign = ((PsiPrefixExpression)parent).getOperationSign().getText(); + return new TypeConversionDescriptor(sign + "$qualifier$[$idx$]", "$qualifier$.set($idx$, " + + getBoxedWrapper(from, to, + "$qualifier$.get($idx$) " + sign.charAt(0) + " 1") + + ")", (PsiExpression)parent); + } + else if (parent instanceof PsiBinaryExpression) { + final String sign = ((PsiBinaryExpression)parent).getOperationSign().getText(); + return new TypeConversionDescriptor("$qualifier$[$idx$]" + sign + "$val$", "$qualifier$.set($idx$, " + + getBoxedWrapper(from, to, "$qualifier$.get($idx$) " + + sign + + " $val$)") + + ")", (PsiExpression)parent); + } + } + + if (context instanceof PsiArrayAccessExpression) { + return new TypeConversionDescriptor("$qualifier$[$idx$]", "$qualifier$.get($idx$)", (PsiExpression)context); + } + return null; + } + + private static String getBoxedWrapper(final PsiType from, final PsiType to, @NotNull String arg) { + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(to); + final PsiClass atomicClass = resolveResult.getElement(); + LOG.assertTrue(atomicClass != null); + final PsiTypeParameter[] typeParameters = atomicClass.getTypeParameters(); + if (typeParameters.length == 1) { + final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); + LOG.assertTrue(substitutor.isValid()); + final PsiType initial = substitutor.substitute(typeParameters[0]); + final PsiPrimitiveType unboxedInitialType = PsiPrimitiveType.getUnboxedType(initial); + if (unboxedInitialType != null) { + LOG.assertTrue(initial != null); + if (from instanceof PsiPrimitiveType) { + final PsiClassType boxedFromType = ((PsiPrimitiveType)from).getBoxedType(atomicClass); + LOG.assertTrue(boxedFromType != null); + return "new " + initial.getPresentableText() + "((" + unboxedInitialType.getCanonicalText() + ")(" + arg + "))"; + } + } + } + return arg; + } + + @Nullable + private static TypeConversionDescriptor findReverseConversion(PsiElement context) { + if (context instanceof PsiReferenceExpression) { + if (context.getParent() instanceof PsiMethodCallExpression) { + return findReverseConversionForMethodCall(context); + } + } + else if (context instanceof PsiNewExpression) { + return new TypeConversionDescriptor("new $type$($qualifier$)", "$qualifier$"); + } + else if (context instanceof PsiMethodCallExpression) { + return findReverseConversionForMethodCall(((PsiMethodCallExpression)context).getMethodExpression()); + } + return null; + } + + @Nullable + private static TypeConversionDescriptor findReverseConversionForMethodCall(PsiElement context) { + final PsiElement resolved = ((PsiReferenceExpression)context).resolve(); + if (resolved instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)resolved; + final int parametersCount = method.getParameterList().getParametersCount(); + final String resolvedName = method.getName(); + if (Comparing.strEqual(resolvedName, "get")) { + return parametersCount == 0 ? + new TypeConversionDescriptor("$qualifier$.get()", "$qualifier$") : + new TypeConversionDescriptor("$qualifier$.get($idx$)", "$qualifier$[$idx$]"); + } + else if (Comparing.strEqual(resolvedName, "set")) { + return parametersCount == 1 ? + new TypeConversionDescriptor("$qualifier$.set($val$)", "$qualifier$ = $val$") : + new TypeConversionDescriptor("$qualifier$.set($idx$, $val$)", "$qualifier$[$idx$] = $val$"); + } + else if (Comparing.strEqual(resolvedName, "addAndGet")) { + return parametersCount == 1 ? + new TypeConversionDescriptor("$qualifier$.addAndGet($delta$)", "$qualifier$ + $delta$") : + new TypeConversionDescriptor("$qualifier$.addAndGet($idx$, $delta$)", "$qualifier$[$idx$] + $delta$"); + } + else if (Comparing.strEqual(resolvedName, "incrementAndGet")) { + return parametersCount == 0 ? + new TypeConversionDescriptor("$qualifier$.incrementAndGet()", "++$qualifier$") : + new TypeConversionDescriptor("$qualifier$.incrementAndGet($idx$)", "++$qualifier$[$idx$]"); + } + else if (Comparing.strEqual(resolvedName, "decrementAndGet")) { + return parametersCount == 0 ? + new TypeConversionDescriptor("$qualifier$.decrementAndGet()", "--$qualifier$") : + new TypeConversionDescriptor("$qualifier$.decrementAndGet($idx$)", "--$qualifier$[$idx$]"); + } + else if (Comparing.strEqual(resolvedName, "getAndIncrement")) { + return parametersCount == 0 ? + new TypeConversionDescriptor("$qualifier$.getAndIncrement()", "$qualifier$++") : + new TypeConversionDescriptor("$qualifier$.getAndIncrement($idx$)", "$qualifier$[$idx$]++"); + } + else if (Comparing.strEqual(resolvedName, "getAndDecrement")) { + return parametersCount == 0 ? + new TypeConversionDescriptor("$qualifier$.getAndDecrement()", "$qualifier$--") : + new TypeConversionDescriptor("$qualifier$.getAndDecrement($idx$)", "$qualifier$[$idx$]--"); + } + else if (Comparing.strEqual(resolvedName, "getAndAdd")) { + return parametersCount == 1? + new TypeConversionDescriptor("$qualifier$.getAndAdd($val$)", "$qualifier$ += $val$") : + new TypeConversionDescriptor("$qualifier$.getAndAdd($idx$, $val$)", "$qualifier$[$idx$] += $val$"); + } + else if (Comparing.strEqual(resolvedName, "getAndSet")) { + return parametersCount == 1 ? + new TypeConversionDescriptor("$qualifier$.getAndSet($val$)", "$qualifier$ = $val$") : + new TypeConversionDescriptor("$qualifier$.getAndSet($idx$, $val$)", "$qualifier$[$idx$] = $val$"); + } + } + return null; + } + +} diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/BoxingTypeConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/BoxingTypeConversionRule.java new file mode 100644 index 000000000000..4353bf602e04 --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/BoxingTypeConversionRule.java @@ -0,0 +1,36 @@ +/* + * User: anna + * Date: 08-Aug-2008 + */ +package com.intellij.refactoring.typeMigration.rules; + +import com.intellij.openapi.util.Comparing; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; +import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase; +import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; + +public class BoxingTypeConversionRule extends TypeConversionRule { + + public TypeConversionDescriptorBase findConversion(final PsiType from, final PsiType to, final PsiMember member, final PsiExpression context, + final TypeMigrationLabeler labeler) { + if (to instanceof PsiClassType && from instanceof PsiPrimitiveType) { + if (!PsiUtil.isLanguageLevel5OrHigher(context)) { + final String boxedTypeName = ((PsiPrimitiveType)from).getBoxedTypeName(); + if (Comparing.strEqual(boxedTypeName, to.getCanonicalText())) { + return new TypeConversionDescriptor("$qualifier$", boxedTypeName + ".valueOf($qualifier$)"); + } + } + } + else if (from instanceof PsiClassType && to instanceof PsiPrimitiveType) { + if (!PsiUtil.isLanguageLevel5OrHigher(context)) { + final String boxedTypeName = ((PsiPrimitiveType)to).getBoxedTypeName(); + if (Comparing.strEqual(boxedTypeName, from.getCanonicalText())) { + return new TypeConversionDescriptor("$qualifier$", "($qualifier$)." + to.getCanonicalText() + "Value()"); + } + } + } + return null; + } +}
\ No newline at end of file diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ElementToArrayConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ElementToArrayConversionRule.java new file mode 100644 index 000000000000..0d25df778bcc --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ElementToArrayConversionRule.java @@ -0,0 +1,36 @@ +/* + * User: anna + * Date: 25-Aug-2008 + */ +package com.intellij.refactoring.typeMigration.rules; + +import com.intellij.psi.*; +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; + +public class ElementToArrayConversionRule extends TypeConversionRule{ + public TypeConversionDescriptorBase findConversion(final PsiType from, final PsiType to, final PsiMember member, final PsiExpression context, + final TypeMigrationLabeler labeler) { + if (member == null && to instanceof PsiArrayType && TypeConversionUtil.isAssignable(((PsiArrayType)to).getComponentType(), from)) { + TypeConversionDescriptor wrapDescription = + new TypeConversionDescriptor("$qualifier$", "new " + from.getCanonicalText() + "[]{$qualifier$}", context); + if (((PsiArrayType)to).getComponentType() instanceof PsiClassType && from instanceof PsiPrimitiveType) { + final String boxedTypeName = ((PsiPrimitiveType)from).getBoxedTypeName(); + final String normalizedArrayInitializer = + PsiUtil.isLanguageLevel5OrHigher(context) ? "$qualifier$" : boxedTypeName + ".valueOf($qualifier$)"; + wrapDescription = new TypeConversionDescriptor("$qualifier$", "new " + boxedTypeName + "[]{" + normalizedArrayInitializer + "}", context); + } + final PsiElement parent = context.getParent(); + if ((context instanceof PsiLiteralExpression || context instanceof PsiReferenceExpression) && parent instanceof PsiStatement) { + return wrapDescription; + } + if (parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)parent).getRExpression() == context) { + return wrapDescription; + } + } + return null; + } +}
\ No newline at end of file diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ListArrayConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ListArrayConversionRule.java new file mode 100644 index 000000000000..0aa639ba28cb --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ListArrayConversionRule.java @@ -0,0 +1,177 @@ +/* + * User: anna + * Date: 08-Aug-2008 + */ +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 && member.getName().equals("length")) { + 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); + if (classResolveResult != null) { + 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; + } + +}
\ No newline at end of file diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java new file mode 100644 index 000000000000..2fb305147d02 --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java @@ -0,0 +1,231 @@ +/* + * 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); + } + + +}
\ No newline at end of file |