| /* |
| * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.siyeh.ig.jdk; |
| |
| import com.intellij.codeInspection.ProblemDescriptor; |
| import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; |
| import com.intellij.lang.java.JavaLanguage; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.psi.*; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.psi.util.TypeConversionUtil; |
| import com.siyeh.InspectionGadgetsBundle; |
| import com.siyeh.ig.BaseInspection; |
| import com.siyeh.ig.BaseInspectionVisitor; |
| import com.siyeh.ig.InspectionGadgetsFix; |
| import com.siyeh.ig.PsiReplacementUtil; |
| import com.siyeh.ig.psiutils.*; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| public class AutoBoxingInspection extends BaseInspection { |
| |
| @SuppressWarnings({"PublicField"}) |
| public boolean ignoreAddedToCollection = false; |
| |
| /** |
| * @noinspection StaticCollection |
| */ |
| @NonNls static final Map<String, String> s_boxingClasses = new HashMap<String, String>(8); |
| |
| static { |
| s_boxingClasses.put("byte", CommonClassNames.JAVA_LANG_BYTE); |
| s_boxingClasses.put("short", CommonClassNames.JAVA_LANG_SHORT); |
| s_boxingClasses.put("int", CommonClassNames.JAVA_LANG_INTEGER); |
| s_boxingClasses.put("long", CommonClassNames.JAVA_LANG_LONG); |
| s_boxingClasses.put("float", CommonClassNames.JAVA_LANG_FLOAT); |
| s_boxingClasses.put("double", CommonClassNames.JAVA_LANG_DOUBLE); |
| s_boxingClasses.put("boolean", CommonClassNames.JAVA_LANG_BOOLEAN); |
| s_boxingClasses.put("char", CommonClassNames.JAVA_LANG_CHARACTER); |
| } |
| |
| @NonNls static final Set<String> convertableBoxedClassNames = new HashSet<String>(); |
| |
| static { |
| convertableBoxedClassNames.add(CommonClassNames.JAVA_LANG_BYTE); |
| convertableBoxedClassNames.add(CommonClassNames.JAVA_LANG_CHARACTER); |
| convertableBoxedClassNames.add(CommonClassNames.JAVA_LANG_SHORT); |
| } |
| |
| @Override |
| @NotNull |
| public String getDisplayName() { |
| return InspectionGadgetsBundle.message("auto.boxing.display.name"); |
| } |
| |
| @Override |
| public String getAlternativeID() { |
| return "boxing"; |
| } |
| |
| @Override |
| @NotNull |
| public String buildErrorString(Object... infos) { |
| return InspectionGadgetsBundle.message("auto.boxing.problem.descriptor"); |
| } |
| |
| @Override |
| @Nullable |
| public JComponent createOptionsPanel() { |
| return new SingleCheckboxOptionsPanel(InspectionGadgetsBundle.message("auto.boxing.ignore.added.to.collection.option"), this, |
| "ignoreAddedToCollection"); |
| } |
| |
| @Override |
| public BaseInspectionVisitor buildVisitor() { |
| return new AutoBoxingVisitor(); |
| } |
| |
| @Override |
| public InspectionGadgetsFix buildFix(Object... infos) { |
| return new AutoBoxingFix(); |
| } |
| |
| private static class AutoBoxingFix extends InspectionGadgetsFix { |
| |
| @Override |
| @NotNull |
| public String getName() { |
| return InspectionGadgetsBundle.message("auto.boxing.make.boxing.explicit.quickfix"); |
| } |
| |
| @NotNull |
| @Override |
| public String getFamilyName() { |
| return getName(); |
| } |
| |
| @Override |
| public void doFix(Project project, ProblemDescriptor descriptor) { |
| final PsiExpression expression = (PsiExpression)descriptor.getPsiElement(); |
| final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false, true); |
| if (expectedType == null) { |
| return; |
| } |
| final String expectedTypeText = expectedType.getCanonicalText(); |
| final String classToConstruct; |
| if (s_boxingClasses.containsValue(expectedTypeText)) { |
| classToConstruct = expectedTypeText; |
| } |
| else { |
| final PsiType type = expression.getType(); |
| if (type == null) { |
| return; |
| } |
| final String expressionTypeText = type.getCanonicalText(); |
| classToConstruct = s_boxingClasses.get(expressionTypeText); |
| } |
| if (shortcutReplace(expression, classToConstruct)) { |
| return; |
| } |
| final PsiExpression strippedExpression = ParenthesesUtils.stripParentheses(expression); |
| if (strippedExpression == null) { |
| return; |
| } |
| @NonNls final String expressionText = strippedExpression.getText(); |
| @NonNls final String newExpression; |
| if ("true".equals(expressionText)) { |
| newExpression = "java.lang.Boolean.TRUE"; |
| } |
| else if ("false".equals(expressionText)) { |
| newExpression = "java.lang.Boolean.FALSE"; |
| } |
| else { |
| newExpression = classToConstruct + ".valueOf(" + expressionText + ')'; |
| } |
| final PsiElement parent = expression.getParent(); |
| if (parent instanceof PsiTypeCastExpression) { |
| final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)parent; |
| PsiReplacementUtil.replaceExpression(typeCastExpression, newExpression); |
| } else { |
| PsiReplacementUtil.replaceExpression(expression, newExpression); |
| } |
| } |
| |
| private static boolean shortcutReplace(PsiExpression expression, String classToConstruct) { |
| if (!(expression instanceof PsiMethodCallExpression)) { |
| return false; |
| } |
| final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; |
| final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); |
| final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); |
| if (qualifierExpression == null) { |
| return false; |
| } |
| if (classToConstruct.equals(CommonClassNames.JAVA_LANG_INTEGER)) { |
| if (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_INTEGER, PsiType.INT, "intValue")) { |
| expression.replace(qualifierExpression); |
| return true; |
| } |
| } |
| else if (classToConstruct.equals(CommonClassNames.JAVA_LANG_SHORT)) { |
| if (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_SHORT, PsiType.SHORT, "shortValue")) { |
| expression.replace(qualifierExpression); |
| return true; |
| } |
| } |
| else if (classToConstruct.equals(CommonClassNames.JAVA_LANG_BYTE)) { |
| if (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_BYTE, PsiType.BYTE, "byteValue")) { |
| expression.replace(qualifierExpression); |
| return true; |
| } |
| } |
| else if (classToConstruct.equals(CommonClassNames.JAVA_LANG_CHARACTER)) { |
| if (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_CHARACTER, PsiType.CHAR, "charValue")) { |
| expression.replace(qualifierExpression); |
| return true; |
| } |
| } |
| else if (classToConstruct.equals(CommonClassNames.JAVA_LANG_LONG)) { |
| if (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_LONG, PsiType.LONG, "longValue")) { |
| expression.replace(qualifierExpression); |
| return true; |
| } |
| } |
| else if (classToConstruct.equals(CommonClassNames.JAVA_LANG_FLOAT)) { |
| if (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_FLOAT, PsiType.FLOAT, "floatValue")) { |
| expression.replace(qualifierExpression); |
| return true; |
| } |
| } |
| else if (classToConstruct.equals(CommonClassNames.JAVA_LANG_DOUBLE)) { |
| if (MethodCallUtils.isCallToMethod(methodCallExpression, CommonClassNames.JAVA_LANG_DOUBLE, PsiType.DOUBLE, "doubleValue")) { |
| expression.replace(qualifierExpression); |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| private class AutoBoxingVisitor extends BaseInspectionVisitor { |
| |
| @Override |
| public void visitElement(PsiElement element) { |
| if (element.getLanguage() != JavaLanguage.INSTANCE) { |
| return; |
| } |
| if (!PsiUtil.isLanguageLevel5OrHigher(element)) { |
| return; |
| } |
| super.visitElement(element); |
| } |
| |
| @Override |
| public void visitArrayAccessExpression(PsiArrayAccessExpression expression) { |
| super.visitArrayAccessExpression(expression); |
| checkExpression(expression); |
| } |
| |
| @Override |
| public void visitAssignmentExpression(PsiAssignmentExpression expression) { |
| super.visitAssignmentExpression(expression); |
| checkExpression(expression); |
| } |
| |
| @Override |
| public void visitConditionalExpression(PsiConditionalExpression expression) { |
| super.visitConditionalExpression(expression); |
| checkExpression(expression); |
| } |
| |
| @Override |
| public void visitInstanceOfExpression(PsiInstanceOfExpression expression) { |
| super.visitInstanceOfExpression(expression); |
| checkExpression(expression); |
| } |
| |
| @Override |
| public void visitLiteralExpression(PsiLiteralExpression expression) { |
| super.visitLiteralExpression(expression); |
| checkExpression(expression); |
| } |
| |
| @Override |
| public void visitMethodCallExpression(PsiMethodCallExpression expression) { |
| super.visitMethodCallExpression(expression); |
| checkExpression(expression); |
| } |
| |
| @Override |
| public void visitParenthesizedExpression(PsiParenthesizedExpression expression) { |
| super.visitParenthesizedExpression(expression); |
| checkExpression(expression); |
| } |
| |
| @Override |
| public void visitPolyadicExpression(PsiPolyadicExpression expression) { |
| super.visitPolyadicExpression(expression); |
| checkExpression(expression); |
| } |
| |
| @Override |
| public void visitPostfixExpression(PsiPostfixExpression expression) { |
| super.visitPostfixExpression(expression); |
| checkExpression(expression); |
| } |
| |
| @Override |
| public void visitPrefixExpression(PsiPrefixExpression expression) { |
| super.visitPrefixExpression(expression); |
| checkExpression(expression); |
| } |
| |
| @Override |
| public void visitReferenceExpression(PsiReferenceExpression expression) { |
| super.visitReferenceExpression(expression); |
| checkExpression(expression); |
| } |
| |
| @Override |
| public void visitTypeCastExpression(PsiTypeCastExpression expression) { |
| super.visitTypeCastExpression(expression); |
| checkExpression(expression); |
| } |
| |
| private void checkExpression(@NotNull PsiExpression expression) { |
| if (expression.getParent() instanceof PsiParenthesizedExpression) { |
| return; |
| } |
| final PsiType expressionType = expression.getType(); |
| if (expressionType == null || expressionType.equals(PsiType.VOID) || !TypeConversionUtil.isPrimitiveAndNotNull(expressionType)) { |
| return; |
| } |
| final PsiPrimitiveType primitiveType = (PsiPrimitiveType)expressionType; |
| final PsiClassType boxedType = primitiveType.getBoxedType(expression); |
| if (boxedType == null) { |
| return; |
| } |
| final PsiType expectedType = ExpectedTypeUtils.findExpectedType(expression, false, true); |
| if (expectedType == null || ClassUtils.isPrimitive(expectedType)) { |
| return; |
| } |
| if (!expectedType.isAssignableFrom(boxedType)) { |
| // JLS 5.2 Assignment Conversion |
| // check if a narrowing primitive conversion is applicable |
| if (!(expectedType instanceof PsiClassType) || !PsiUtil.isConstantExpression(expression)) { |
| return; |
| } |
| final PsiClassType classType = (PsiClassType)expectedType; |
| final String className = classType.getCanonicalText(); |
| if (!convertableBoxedClassNames.contains(className)) { |
| return; |
| } |
| if (!PsiType.BYTE.equals(expressionType) && !PsiType.CHAR.equals(expressionType) && |
| !PsiType.SHORT.equals(expressionType) && !PsiType.INT.equals(expressionType)) { |
| return; |
| } |
| } |
| if (ignoreAddedToCollection && isAddedToCollection(expression)) { |
| return; |
| } |
| registerError(expression); |
| } |
| |
| private boolean isAddedToCollection(PsiExpression expression) { |
| final PsiElement parent = expression.getParent(); |
| if (!(parent instanceof PsiExpressionList)) { |
| return false; |
| } |
| final PsiExpressionList expressionList = (PsiExpressionList)parent; |
| final PsiElement grandParent = expressionList.getParent(); |
| if (!(grandParent instanceof PsiMethodCallExpression)) { |
| return false; |
| } |
| final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent; |
| final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); |
| @NonNls final String methodName = methodExpression.getReferenceName(); |
| if (!"put".equals(methodName) && !"set".equals(methodName) && !"add".equals(methodName)) { |
| return false; |
| } |
| final PsiExpression qualifier = methodExpression.getQualifierExpression(); |
| return TypeUtils.expressionHasTypeOrSubtype(qualifier, CommonClassNames.JAVA_UTIL_COLLECTION, CommonClassNames.JAVA_UTIL_MAP) != null; |
| } |
| } |
| } |