blob: 430f2182e5168bdf70446989e7bf4c6d38975c7e [file] [log] [blame]
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.refactoring.util.RefactoringUtil;
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);
psiVariable.normalizeDeclaration();
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);
}
}
}
PsiExpression initializer = psiVariable.getInitializer();
if (initializer != null) {
if (initializer instanceof PsiArrayInitializerExpression) {
PsiExpression normalizedExpr =
RefactoringUtil.createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression)initializer, psiVariable.getType());
initializer = (PsiExpression)initializer.replace(normalizedExpr);
}
final TypeConversionDescriptor directConversion = AtomicConversionRule.wrapWithNewExpression(toType, fromType, initializer, 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;
}
}