diff options
Diffstat (limited to 'java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java')
-rw-r--r-- | java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java | 218 |
1 files changed, 218 insertions, 0 deletions
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; + } +} |