[platform] mock update server
[idea/community.git] / java / typeMigration / src / com / intellij / refactoring / typeMigration / rules / AtomicConversionRule.java
1 /*
2  * User: anna
3  * Date: 18-Aug-2009
4  */
5 package com.intellij.refactoring.typeMigration.rules;
6
7 import com.intellij.openapi.diagnostic.Logger;
8 import com.intellij.openapi.util.Comparing;
9 import com.intellij.psi.*;
10 import com.intellij.psi.impl.PsiDiamondTypeUtil;
11 import com.intellij.psi.tree.IElementType;
12 import com.intellij.psi.util.PsiUtil;
13 import com.intellij.psi.util.TypeConversionUtil;
14 import com.intellij.refactoring.typeMigration.TypeConversionDescriptor;
15 import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
16 import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
17 import org.jetbrains.annotations.NotNull;
18 import org.jetbrains.annotations.Nullable;
19
20 import java.util.concurrent.atomic.*;
21
22 public class AtomicConversionRule extends TypeConversionRule {
23   private static final Logger LOG = Logger.getInstance("#" + AtomicConversionRule.class.getName());
24
25
26   @Override
27   public TypeConversionDescriptorBase findConversion(PsiType from,
28                                                  PsiType to,
29                                                  PsiMember member,
30                                                  PsiExpression context,
31                                                  TypeMigrationLabeler labeler) {
32     if (to instanceof PsiClassType && isAtomicTypeMigration(from, (PsiClassType)to, context)) {
33       return findDirectConversion(context, to, from);
34     }
35     else if (from instanceof PsiClassType && isAtomicTypeMigration(to, (PsiClassType)from, context)) {
36       return findReverseConversion(context);
37     }
38     return null;
39   }
40
41   private static boolean isAtomicTypeMigration(PsiType from, PsiClassType to, PsiExpression context) {
42     if (PsiType.INT.equals(from) && to.getCanonicalText().equals(AtomicInteger.class.getName())) {
43       return true;
44     }
45     if (from.equals(PsiType.INT.createArrayType()) && to.getCanonicalText().equals(AtomicIntegerArray.class.getName())) {
46       return true;
47     }
48     if (PsiType.LONG.equals(from) && to.getCanonicalText().equals(AtomicLong.class.getName())) {
49       return true;
50     }
51     if (from.equals(PsiType.LONG.createArrayType()) && to.getCanonicalText().equals(AtomicLongArray.class.getName())) {
52       return true;
53     }
54     if (PsiType.BOOLEAN.equals(from) && to.getCanonicalText().equals(AtomicBoolean.class.getName())) {
55       return true;
56     }
57     final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(to);
58     final PsiClass atomicClass = resolveResult.getElement();
59
60     if (atomicClass != null) {
61       final String typeQualifiedName = atomicClass.getQualifiedName();
62       if (!Comparing.strEqual(typeQualifiedName, AtomicReference.class.getName()) &&
63           !Comparing.strEqual(typeQualifiedName, AtomicReferenceArray.class.getName())) {
64         return false;
65       }
66       final PsiTypeParameter[] typeParameters = atomicClass.getTypeParameters();
67       if (typeParameters.length != 1) return false;
68       final PsiType toTypeParameterValue = resolveResult.getSubstitutor().substitute(typeParameters[0]);
69       if (toTypeParameterValue != null) {
70         if (from.getDeepComponentType() instanceof PsiPrimitiveType) {
71           final PsiPrimitiveType unboxedInitialType = PsiPrimitiveType.getUnboxedType(toTypeParameterValue);
72           if (unboxedInitialType != null) {
73             return TypeConversionUtil.areTypesConvertible(from.getDeepComponentType(), unboxedInitialType);
74           }
75         }
76         else {
77           return TypeConversionUtil.isAssignable(from.getDeepComponentType(), PsiUtil.captureToplevelWildcards(toTypeParameterValue, context));
78         }
79       }
80     }
81     return false;
82   }
83
84   @Nullable
85   public static TypeConversionDescriptor findDirectConversion(PsiElement context, PsiType to, PsiType from) {
86     final PsiClass toTypeClass = PsiUtil.resolveClassInType(to);
87     LOG.assertTrue(toTypeClass != null);
88     final String qualifiedName = toTypeClass.getQualifiedName();
89     if (qualifiedName != null) {
90       if (qualifiedName.equals(AtomicInteger.class.getName()) || qualifiedName.equals(AtomicLong.class.getName())) {
91
92         if (context instanceof PsiPostfixExpression) {
93           final IElementType operationSign = ((PsiPostfixExpression)context).getOperationTokenType();
94           if (operationSign == JavaTokenType.MINUSMINUS) {
95             return new TypeConversionDescriptor("$qualifier$--", "$qualifier$.getAndDecrement()");
96           }
97           if (operationSign == JavaTokenType.PLUSPLUS) {
98             return new TypeConversionDescriptor("$qualifier$++", "$qualifier$.getAndIncrement()");
99           }
100
101         }
102         else if (context instanceof PsiPrefixExpression) {
103           final IElementType operationSign = ((PsiPrefixExpression)context).getOperationTokenType();
104           if (operationSign == JavaTokenType.MINUSMINUS) {
105             return new TypeConversionDescriptor("--$qualifier$", "$qualifier$.decrementAndGet()");
106           }
107           if (operationSign == JavaTokenType.PLUSPLUS) {
108             return new TypeConversionDescriptor("++$qualifier$", "$qualifier$.incrementAndGet()");
109           }
110
111         }
112         else if (context instanceof PsiAssignmentExpression) {
113           final PsiJavaToken signToken = ((PsiAssignmentExpression)context).getOperationSign();
114           final IElementType operationSign = signToken.getTokenType();
115           final String sign = signToken.getText();
116           if (operationSign == JavaTokenType.PLUSEQ || operationSign == JavaTokenType.MINUSEQ) {
117             return new TypeConversionDescriptor("$qualifier$ " + sign + " $val$", "$qualifier$.getAndAdd(" +
118                                                                                   (operationSign == JavaTokenType.MINUSEQ ? "-" : "") +
119                                                                                   "($val$))");
120           }
121         }
122       }
123       else if (qualifiedName.equals(AtomicIntegerArray.class.getName()) || qualifiedName.equals(AtomicLongArray.class.getName())) {
124         PsiElement parentExpression = context.getParent();
125         if (parentExpression instanceof PsiPostfixExpression) {
126           final IElementType operationSign = ((PsiPostfixExpression)parentExpression).getOperationTokenType();
127           if (operationSign == JavaTokenType.MINUSMINUS) {
128             return new TypeConversionDescriptor("$qualifier$[$idx$]--", "$qualifier$.getAndDecrement($idx$)",
129                                                 (PsiExpression)parentExpression);
130           }
131           if (operationSign == JavaTokenType.PLUSPLUS) {
132             return new TypeConversionDescriptor("$qualifier$[$idx$]++", "$qualifier$.getAndIncrement($idx$)",
133                                                 (PsiExpression)parentExpression);
134           }
135
136         }
137         else if (parentExpression instanceof PsiPrefixExpression) {
138           final IElementType operationSign = ((PsiPrefixExpression)parentExpression).getOperationTokenType();
139           if (operationSign == JavaTokenType.MINUSMINUS) {
140             return new TypeConversionDescriptor("--$qualifier$[$idx$]", "$qualifier$.decrementAndGet($idx$)",
141                                                 (PsiExpression)parentExpression);
142           }
143           if (operationSign == JavaTokenType.PLUSPLUS) {
144             return new TypeConversionDescriptor("++$qualifier$[$idx$]", "$qualifier$.incrementAndGet($idx$)",
145                                                 (PsiExpression)parentExpression);
146           }
147
148         }
149         else if (parentExpression instanceof PsiAssignmentExpression) {
150           final PsiJavaToken signToken = ((PsiAssignmentExpression)parentExpression).getOperationSign();
151           final IElementType operationSign = signToken.getTokenType();
152           final String sign = signToken.getText();
153           if (operationSign == JavaTokenType.PLUSEQ || operationSign == JavaTokenType.MINUSEQ) {
154             return new TypeConversionDescriptor("$qualifier$[$idx$] " + sign + " $val$", "$qualifier$.getAndAdd($idx$, " +
155                                                                                          (operationSign == JavaTokenType.MINUSEQ
156                                                                                           ? "-"
157                                                                                           : "") +
158                                                                                          "($val$))", (PsiExpression)parentExpression);
159           }
160         }
161       }
162     }
163     return from instanceof PsiArrayType
164            ? findDirectConversionForAtomicReferenceArray(context, to, from)
165            : findDirectConversionForAtomicReference(context, to, from);
166   }
167
168   @Nullable
169   private static TypeConversionDescriptor findDirectConversionForAtomicReference(PsiElement context, PsiType to, PsiType from) {
170     final PsiElement parent = context.getParent();
171     if (parent instanceof PsiAssignmentExpression) {
172       final IElementType operationSign = ((PsiAssignmentExpression)parent).getOperationTokenType();
173       if (operationSign == JavaTokenType.EQ) {
174         return new TypeConversionDescriptor("$qualifier$ = $val$", "$qualifier$.set($val$)", (PsiAssignmentExpression)parent);
175       }
176     }
177
178     if (context instanceof PsiReferenceExpression) {
179       final PsiExpression qualifierExpression = ((PsiReferenceExpression)context).getQualifierExpression();
180       final PsiExpression expression = context.getParent() instanceof PsiMethodCallExpression && qualifierExpression != null
181                                        ? qualifierExpression
182                                        : (PsiExpression)context;
183       return new TypeConversionDescriptor("$qualifier$", "$qualifier$.get()", expression);
184     }
185     else if (context instanceof PsiAssignmentExpression) {
186       final PsiJavaToken signToken = ((PsiAssignmentExpression)context).getOperationSign();
187       final IElementType operationSign = signToken.getTokenType();
188       final String sign = signToken.getText();
189       if (parent instanceof PsiExpressionStatement) {
190         if (operationSign == JavaTokenType.EQ) {
191           final PsiExpression lExpression = ((PsiAssignmentExpression)context).getLExpression();
192           if (lExpression instanceof PsiReferenceExpression) {
193             final PsiElement element = ((PsiReferenceExpression)lExpression).resolve();
194             if (element instanceof PsiVariable && ((PsiVariable)element).hasModifierProperty(PsiModifier.FINAL)) {
195               return wrapWithNewExpression(to, from, ((PsiAssignmentExpression)context).getRExpression(), element);
196             }
197           }
198           return new TypeConversionDescriptor("$qualifier$ = $val$", "$qualifier$.set($val$)");
199         }
200         else {
201           return new TypeConversionDescriptor("$qualifier$" + sign + "$val$", "$qualifier$.set(" +
202                                                                               getBoxedWrapper(from, to, "$qualifier$.get() " +
203                                                                                                         sign.charAt(0) +
204                                                                                                         " $val$") +
205                                                                               ")");
206         }
207       } //else should be a conflict
208     }
209     else if (context instanceof PsiPostfixExpression) {
210       final String sign = ((PsiPostfixExpression)context).getOperationSign().getText();
211       return new TypeConversionDescriptor("$qualifier$" + sign, "$qualifier$.getAndSet(" +
212                                                                 getBoxedWrapper(from, to, "$qualifier$.get() " + sign.charAt(0) + " 1") +
213                                                                 ")");
214     }
215     else if (context instanceof PsiPrefixExpression) {
216       final PsiJavaToken operationSign = ((PsiPrefixExpression)context).getOperationSign();
217       if (operationSign.getTokenType() == JavaTokenType.EXCL) {
218         return new TypeConversionDescriptor("!$qualifier$", "!$qualifier$.get()");
219       }
220       final String sign = operationSign.getText();
221       return new TypeConversionDescriptor(sign + "$qualifier$", "$qualifier$.set(" +  //todo reject?
222                                                                 getBoxedWrapper(from, to, "$qualifier$.get() " + sign.charAt(0) + " 1") +
223                                                                 ")");
224     } else if (context instanceof PsiBinaryExpression) {
225       final String sign = ((PsiBinaryExpression)context).getOperationSign().getText();
226       return new TypeConversionDescriptor("$qualifier$" + sign + "$val$", "$qualifier$.get() " + sign + " $val$");
227     }
228
229     if (parent instanceof PsiVariable) {
230       return wrapWithNewExpression(to, from, null, parent);
231     }
232     return null;
233   }
234
235   public static TypeConversionDescriptor wrapWithNewExpression(PsiType to, PsiType from, @Nullable PsiExpression expression, PsiElement context) {
236     final String typeText = PsiDiamondTypeUtil.getCollapsedType(to, context);
237     final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(to);
238     final PsiClass atomicClass = resolveResult.getElement();
239     LOG.assertTrue(atomicClass != null);
240     final PsiTypeParameter[] typeParameters = atomicClass.getTypeParameters();
241     if (typeParameters.length == 1) {
242       final PsiType initial = resolveResult.getSubstitutor().substitute(typeParameters[0]);
243       final PsiPrimitiveType unboxedInitialType = PsiPrimitiveType.getUnboxedType(initial);
244       if (unboxedInitialType != null) {
245         LOG.assertTrue(initial != null);
246         if (from instanceof PsiPrimitiveType) {
247           final PsiClassType boxedFromType = ((PsiPrimitiveType)from).getBoxedType(atomicClass);
248           LOG.assertTrue(boxedFromType != null);
249           if (!TypeConversionUtil.isAssignable(initial, boxedFromType)) {
250             return new TypeConversionDescriptor("$val$", "new " + typeText + "((" + unboxedInitialType.getCanonicalText() + ")$val$)", expression);
251           }
252         }
253       }
254     }
255     return new TypeConversionDescriptor("$val$", "new " + typeText + "($val$)", expression);
256   }
257
258   @Nullable
259   private static TypeConversionDescriptor findDirectConversionForAtomicReferenceArray(PsiElement context, PsiType to, PsiType from) {
260     LOG.assertTrue(from instanceof PsiArrayType);
261     from = ((PsiArrayType)from).getComponentType();
262     final PsiElement parent = context.getParent();
263     final PsiElement parentParent = parent.getParent();
264
265     if (parent instanceof PsiAssignmentExpression) {
266       final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent;
267       final IElementType operationSign = assignmentExpression.getOperationTokenType();
268       final String sign = assignmentExpression.getOperationSign().getText();
269       if (context instanceof PsiArrayAccessExpression) {
270         if (parentParent instanceof PsiExpressionStatement) {
271           if (assignmentExpression.getLExpression() == context) {
272             if (operationSign == JavaTokenType.EQ) {
273               return new TypeConversionDescriptor("$qualifier$[$idx$] = $val$", "$qualifier$.set($idx$, $val$)", assignmentExpression);
274             }
275             else {
276               return new TypeConversionDescriptor("$qualifier$[$idx$]" + sign + "$val$",
277                                                   "$qualifier$.set($idx$, " + getBoxedWrapper(from, to, "$qualifier$.get($idx$) " + sign.charAt(0) + " $val$") + ")",
278                                                   assignmentExpression);
279             }
280           }
281         } //else should be a conflict
282       }
283       else {
284         final PsiExpression rExpression = assignmentExpression.getRExpression();
285         if (rExpression == context && operationSign == JavaTokenType.EQ) {   //array = new T[l];
286           return wrapWithNewExpression(to, from, rExpression, context);
287         }
288       }
289     } else if (parent instanceof PsiVariable) {
290       if (((PsiVariable)parent).getInitializer() == context) {
291         return wrapWithNewExpression(to, from, (PsiExpression)context, context);
292       }
293     }
294
295     if (parentParent instanceof PsiExpressionStatement) {
296       if (parent instanceof PsiPostfixExpression) {
297         final String sign = ((PsiPostfixExpression)parent).getOperationSign().getText();
298         return new TypeConversionDescriptor("$qualifier$[$idx$]" + sign, "$qualifier$.getAndSet($idx$, " +
299                                                                          getBoxedWrapper(from, to,
300                                                                                          "$qualifier$.get($idx$) " + sign.charAt(0) + " 1") +
301                                                                          ")", (PsiExpression)parent);
302       }
303       else if (parent instanceof PsiPrefixExpression) {
304         final String sign = ((PsiPrefixExpression)parent).getOperationSign().getText();
305         return new TypeConversionDescriptor(sign + "$qualifier$[$idx$]", "$qualifier$.set($idx$, " +
306                                                                          getBoxedWrapper(from, to,
307                                                                                          "$qualifier$.get($idx$) " + sign.charAt(0) + " 1") +
308                                                                          ")", (PsiExpression)parent);
309       }
310       else if (parent instanceof PsiBinaryExpression) {
311         final String sign = ((PsiBinaryExpression)parent).getOperationSign().getText();
312         return new TypeConversionDescriptor("$qualifier$[$idx$]" + sign + "$val$", "$qualifier$.set($idx$, " +
313                                                                                    getBoxedWrapper(from, to, "$qualifier$.get($idx$) " +
314                                                                                                              sign +
315                                                                                                              " $val$)") +
316                                                                                    ")", (PsiExpression)parent);
317       }
318     }
319
320     if (context instanceof PsiArrayAccessExpression) {
321       return new TypeConversionDescriptor("$qualifier$[$idx$]", "$qualifier$.get($idx$)", (PsiExpression)context);
322     }
323     return null;
324   }
325
326   private static String getBoxedWrapper(final PsiType from, final PsiType to, @NotNull String arg) {
327     final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(to);
328     final PsiClass atomicClass = resolveResult.getElement();
329     LOG.assertTrue(atomicClass != null);
330     final PsiTypeParameter[] typeParameters = atomicClass.getTypeParameters();
331     if (typeParameters.length == 1) {
332       final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
333       LOG.assertTrue(substitutor.isValid());
334       final PsiType initial = substitutor.substitute(typeParameters[0]);
335       final PsiPrimitiveType unboxedInitialType = PsiPrimitiveType.getUnboxedType(initial);
336       if (unboxedInitialType != null) {
337         LOG.assertTrue(initial != null);
338         if (from instanceof PsiPrimitiveType) {
339           final PsiClassType boxedFromType = ((PsiPrimitiveType)from).getBoxedType(atomicClass);
340           LOG.assertTrue(boxedFromType != null);
341           return "new " + initial.getPresentableText() + "((" + unboxedInitialType.getCanonicalText() + ")(" + arg + "))";
342         }
343       }
344     }
345     return arg;
346   }
347
348   @Nullable
349   private static TypeConversionDescriptor findReverseConversion(PsiElement context) {
350     if (context instanceof PsiReferenceExpression) {
351       if (context.getParent() instanceof PsiMethodCallExpression) {
352         return findReverseConversionForMethodCall(context);
353       }
354     }
355     else if (context instanceof PsiNewExpression) {
356       return new TypeConversionDescriptor("new $type$($qualifier$)", "$qualifier$");
357     }
358     else if (context instanceof PsiMethodCallExpression) {
359       return findReverseConversionForMethodCall(((PsiMethodCallExpression)context).getMethodExpression());
360     }
361     return null;
362   }
363
364   @Nullable
365   private static TypeConversionDescriptor findReverseConversionForMethodCall(PsiElement context) {
366     final PsiElement resolved = ((PsiReferenceExpression)context).resolve();
367     if (resolved instanceof PsiMethod) {
368       final PsiMethod method = (PsiMethod)resolved;
369       final int parametersCount = method.getParameterList().getParametersCount();
370       final String resolvedName = method.getName();
371       if (Comparing.strEqual(resolvedName, "get")) {
372         return parametersCount == 0 ?
373                new TypeConversionDescriptor("$qualifier$.get()", "$qualifier$") :
374                new TypeConversionDescriptor("$qualifier$.get($idx$)", "$qualifier$[$idx$]");
375       }
376       else if (Comparing.strEqual(resolvedName, "set")) {
377         return parametersCount == 1 ?
378                new TypeConversionDescriptor("$qualifier$.set($val$)", "$qualifier$ = $val$") :
379                new TypeConversionDescriptor("$qualifier$.set($idx$, $val$)", "$qualifier$[$idx$] = $val$");
380       }
381       else if (Comparing.strEqual(resolvedName, "addAndGet")) {
382         return parametersCount == 1 ?
383                new TypeConversionDescriptor("$qualifier$.addAndGet($delta$)", "$qualifier$ + $delta$") :
384                new TypeConversionDescriptor("$qualifier$.addAndGet($idx$, $delta$)", "$qualifier$[$idx$] + $delta$");
385       }
386       else if (Comparing.strEqual(resolvedName, "incrementAndGet")) {
387         return parametersCount == 0 ?
388                new TypeConversionDescriptor("$qualifier$.incrementAndGet()", "++$qualifier$") :
389                new TypeConversionDescriptor("$qualifier$.incrementAndGet($idx$)", "++$qualifier$[$idx$]");
390       }
391       else if (Comparing.strEqual(resolvedName, "decrementAndGet")) {
392         return parametersCount == 0 ?
393                new TypeConversionDescriptor("$qualifier$.decrementAndGet()", "--$qualifier$") :
394                new TypeConversionDescriptor("$qualifier$.decrementAndGet($idx$)", "--$qualifier$[$idx$]");
395       }
396       else if (Comparing.strEqual(resolvedName, "getAndIncrement")) {
397         return parametersCount == 0 ?
398                new TypeConversionDescriptor("$qualifier$.getAndIncrement()", "$qualifier$++") :
399                new TypeConversionDescriptor("$qualifier$.getAndIncrement($idx$)", "$qualifier$[$idx$]++");
400       }
401       else if (Comparing.strEqual(resolvedName, "getAndDecrement")) {
402         return parametersCount == 0 ?
403                new TypeConversionDescriptor("$qualifier$.getAndDecrement()", "$qualifier$--") :
404                new TypeConversionDescriptor("$qualifier$.getAndDecrement($idx$)", "$qualifier$[$idx$]--");
405       }
406       else if (Comparing.strEqual(resolvedName, "getAndAdd")) {
407         return parametersCount == 1?
408                new TypeConversionDescriptor("$qualifier$.getAndAdd($val$)", "$qualifier$ += $val$") :
409                new TypeConversionDescriptor("$qualifier$.getAndAdd($idx$, $val$)", "$qualifier$[$idx$] += $val$");
410       }
411       else if (Comparing.strEqual(resolvedName, "getAndSet")) {
412         return parametersCount == 1 ?
413                new TypeConversionDescriptor("$qualifier$.getAndSet($val$)", "$qualifier$ = $val$") :
414                new TypeConversionDescriptor("$qualifier$.getAndSet($idx$, $val$)", "$qualifier$[$idx$] = $val$");
415       }
416     }
417     return null;
418   }
419
420 }