IDEADEV-40452
[fedora-idea.git] / plugins / InspectionGadgets / src / com / siyeh / ig / jdk15 / UnnecessaryBoxingInspection.java
blob1e7e3ae6ea635aefb18079d26f88815f5983b299
1 /*
2 * Copyright 2003-2009 Dave Griffith, Bas Leijdekkers
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.siyeh.ig.jdk15;
18 import com.intellij.codeInspection.ProblemDescriptor;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.psi.*;
21 import com.intellij.psi.util.PsiUtil;
22 import com.intellij.psi.util.PsiTreeUtil;
23 import com.intellij.util.IncorrectOperationException;
24 import com.siyeh.InspectionGadgetsBundle;
25 import com.siyeh.ig.BaseInspection;
26 import com.siyeh.ig.BaseInspectionVisitor;
27 import com.siyeh.ig.InspectionGadgetsFix;
28 import com.siyeh.ig.psiutils.MethodCallUtils;
29 import org.jetbrains.annotations.NonNls;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
33 import java.util.HashMap;
34 import java.util.Map;
36 public class UnnecessaryBoxingInspection extends BaseInspection {
38 @NonNls static final Map<String, String> s_boxingArgs =
39 new HashMap<String, String>(9);
41 static {
42 s_boxingArgs.put("java.lang.Integer", "int");
43 s_boxingArgs.put("java.lang.Short", "short");
44 s_boxingArgs.put("java.lang.Boolean", "boolean");
45 s_boxingArgs.put("java.lang.Long", "long");
46 s_boxingArgs.put("java.lang.Byte", "byte");
47 s_boxingArgs.put("java.lang.Float", "float");
48 s_boxingArgs.put("java.lang.Double", "double");
49 s_boxingArgs.put("java.lang.Long", "long");
50 s_boxingArgs.put("java.lang.Character", "char");
53 @Override
54 @NotNull
55 public String getDisplayName() {
56 return InspectionGadgetsBundle.message(
57 "unnecessary.boxing.display.name");
60 @Override
61 public boolean isEnabledByDefault() {
62 return true;
65 @Override
66 @NotNull
67 protected String buildErrorString(Object... infos) {
68 return InspectionGadgetsBundle.message(
69 "unnecessary.boxing.problem.descriptor");
72 @Override
73 public InspectionGadgetsFix buildFix(Object... infos) {
74 return new UnnecessaryBoxingFix();
77 private static class UnnecessaryBoxingFix extends InspectionGadgetsFix {
79 @NotNull
80 public String getName() {
81 return InspectionGadgetsBundle.message(
82 "unnecessary.boxing.remove.quickfix");
85 @Override
86 public void doFix(@NotNull Project project, ProblemDescriptor descriptor)
87 throws IncorrectOperationException {
88 final PsiCallExpression expression =
89 (PsiCallExpression)descriptor.getPsiElement();
90 final PsiType boxedType = expression.getType();
91 final PsiExpressionList argList = expression.getArgumentList();
92 assert argList != null;
93 final PsiExpression[] args = argList.getExpressions();
94 final PsiType argType = args[0].getType();
95 final String cast = getCastString(argType, boxedType);
96 final String newExpression = args[0].getText();
97 replaceExpression(expression, cast + newExpression);
100 private static String getCastString(PsiType fromType,
101 PsiType toType) {
102 final String toTypeText = toType.getCanonicalText();
103 final String fromTypeText = fromType.getCanonicalText();
104 final String unboxedType = s_boxingArgs.get(toTypeText);
105 if (fromTypeText.equals(unboxedType)) {
106 return "";
108 else {
109 return '(' + unboxedType + ')';
114 @Override
115 public BaseInspectionVisitor buildVisitor() {
116 return new UnnecessaryBoxingVisitor();
119 private static class UnnecessaryBoxingVisitor
120 extends BaseInspectionVisitor {
122 @Override public void visitNewExpression(
123 @NotNull PsiNewExpression expression) {
124 if (!PsiUtil.isLanguageLevel5OrHigher(expression)) {
125 return;
127 super.visitNewExpression(expression);
128 final PsiType constructorType = expression.getType();
129 if (constructorType == null) {
130 return;
132 final String constructorTypeText =
133 constructorType.getCanonicalText();
134 if (!s_boxingArgs.containsKey(constructorTypeText)) {
135 return;
137 final PsiMethod constructor = expression.resolveConstructor();
138 if (constructor == null) {
139 return;
141 final PsiParameterList parameterList =
142 constructor.getParameterList();
143 if (parameterList.getParametersCount() != 1) {
144 return;
146 final PsiParameter[] parameters = parameterList.getParameters();
147 final PsiParameter arg = parameters[0];
148 final PsiType argumentType = arg.getType();
149 final String argumentTypeText = argumentType.getCanonicalText();
150 final String boxableConstructorType =
151 s_boxingArgs.get(constructorTypeText);
152 if (!boxableConstructorType.equals(argumentTypeText)) {
153 return;
155 if (!canBeUnboxed(expression)) {
156 return;
158 registerError(expression);
161 @Override public void visitMethodCallExpression(
162 PsiMethodCallExpression expression) {
163 if (!PsiUtil.isLanguageLevel5OrHigher(expression)) {
164 return;
166 super.visitMethodCallExpression(expression);
167 final PsiExpressionList argumentList = expression.getArgumentList();
168 final PsiExpression[] expressions = argumentList.getExpressions();
169 if (expressions.length != 1) {
170 return;
172 if (!(expressions[0].getType() instanceof PsiPrimitiveType)) {
173 return;
175 final PsiReferenceExpression methodExpression =
176 expression.getMethodExpression();
177 @NonNls
178 final String referenceName = methodExpression.getReferenceName();
179 if (referenceName == null || !referenceName.equals("valueOf")) {
180 return;
182 final PsiExpression qualifierExpression =
183 methodExpression.getQualifierExpression();
184 if (!(qualifierExpression instanceof PsiReferenceExpression)) {
185 return;
187 final PsiReferenceExpression referenceExpression =
188 (PsiReferenceExpression)qualifierExpression;
189 final String canonicalText = referenceExpression.getCanonicalText();
190 if (s_boxingArgs.get(canonicalText) == null) {
191 return;
193 if (!canBeUnboxed(expression)) {
194 return;
196 registerError(expression);
199 private static boolean canBeUnboxed(PsiExpression expression) {
200 PsiElement parent = expression.getParent();
201 while (parent instanceof PsiParenthesizedExpression) {
202 parent = parent.getParent();
204 if (parent instanceof PsiExpressionStatement ||
205 parent instanceof PsiReferenceExpression) {
206 return false;
207 } else if (parent instanceof PsiTypeCastExpression) {
208 final PsiTypeCastExpression castExpression =
209 (PsiTypeCastExpression)parent;
210 final PsiType castType = castExpression.getType();
211 if (castType instanceof PsiClassType) {
212 final PsiClassType classType = (PsiClassType)castType;
213 final PsiClass aClass = classType.resolve();
214 if (aClass instanceof PsiTypeParameter) {
215 return false;
218 } else if (parent instanceof PsiConditionalExpression) {
219 final PsiConditionalExpression conditionalExpression =
220 (PsiConditionalExpression)parent;
221 final PsiExpression thenExpression =
222 conditionalExpression.getThenExpression();
223 final PsiExpression elseExpression =
224 conditionalExpression.getElseExpression();
225 if (elseExpression == null || thenExpression == null) {
226 return false;
228 if (PsiTreeUtil.isAncestor(thenExpression, expression, false)) {
229 final PsiType type = elseExpression.getType();
230 return type instanceof PsiPrimitiveType;
231 } else if (PsiTreeUtil.isAncestor(elseExpression, expression,
232 false)) {
233 final PsiType type = thenExpression.getType();
234 return type instanceof PsiPrimitiveType;
235 } else {
236 return true;
239 final PsiMethodCallExpression containingMethodCallExpression =
240 getParentMethodCallExpression(expression);
241 return containingMethodCallExpression == null ||
242 isSameMethodCalledWithoutBoxing(
243 containingMethodCallExpression, expression);
246 @Nullable
247 private static PsiMethodCallExpression getParentMethodCallExpression(
248 @NotNull PsiElement expression) {
249 final PsiElement parent = expression.getParent();
250 if (parent instanceof PsiParenthesizedExpression ||
251 parent instanceof PsiExpressionList) {
252 return getParentMethodCallExpression(parent);
253 } else if (parent instanceof PsiMethodCallExpression) {
254 return (PsiMethodCallExpression)parent;
255 } else {
256 return null;
260 private static boolean isSameMethodCalledWithoutBoxing(
261 @NotNull PsiMethodCallExpression methodCallExpression,
262 @NotNull PsiExpression boxingExpression) {
263 final PsiExpressionList argumentList =
264 methodCallExpression.getArgumentList();
265 final PsiExpression[] expressions = argumentList.getExpressions();
266 final PsiReferenceExpression methodExpression =
267 methodCallExpression.getMethodExpression();
268 final PsiElement element = methodExpression.resolve();
269 if (!(element instanceof PsiMethod)) {
270 return false;
272 final PsiMethod originalMethod = (PsiMethod)element;
273 final String name = originalMethod.getName();
274 final PsiClass containingClass =
275 originalMethod.getContainingClass();
276 if (containingClass == null) {
277 return false;
279 final PsiType[] types = new PsiType[expressions.length];
280 for (int i = 0; i < expressions.length; i++) {
281 final PsiExpression expression = expressions[i];
282 final PsiType type = expression.getType();
283 if (boxingExpression.equals(expression)) {
284 final PsiPrimitiveType unboxedType =
285 PsiPrimitiveType.getUnboxedType(type);
286 if (unboxedType == null) {
287 return false;
289 types[i] = unboxedType;
290 } else {
291 types[i] = type;
294 final PsiMethod[] methods =
295 containingClass.findMethodsByName(name, true);
296 for (PsiMethod method : methods) {
297 if (!originalMethod.equals(method)) {
298 if (MethodCallUtils.isApplicable(method,
299 PsiSubstitutor.EMPTY, types)) {
300 return false;
304 return true;