summaryrefslogtreecommitdiff
path: root/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java
diff options
context:
space:
mode:
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.java218
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;
+ }
+}