summaryrefslogtreecommitdiff
path: root/java/typeMigration/src/com/intellij/refactoring
diff options
context:
space:
mode:
Diffstat (limited to 'java/typeMigration/src/com/intellij/refactoring')
-rw-r--r--java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeConversionDescriptor.java101
-rw-r--r--java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeMigrationVariableTypeFixProvider.java61
-rw-r--r--java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ChangeClassParametersIntention.java124
-rw-r--r--java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java218
-rw-r--r--java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java145
-rw-r--r--java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/AtomicConversionRule.java420
-rw-r--r--java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/BoxingTypeConversionRule.java36
-rw-r--r--java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ElementToArrayConversionRule.java36
-rw-r--r--java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ListArrayConversionRule.java177
-rw-r--r--java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/ThreadLocalConversionRule.java231
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