blob: 67448d56a24125e47bb0e47abb5510e9d7be7fac [file] [log] [blame]
/*
* Copyright 2010-2012 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.bugs;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class StringConcatenationInMessageFormatCallInspection extends BaseInspection {
@Nls
@NotNull
@Override
public String getDisplayName() {
return InspectionGadgetsBundle.message("string.concatenation.in.message.format.call.display.name");
}
@NotNull
@Override
protected String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message("string.concatenation.in.message.format.call.problem.descriptor");
}
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)infos[0];
final String referenceName = referenceExpression.getReferenceName();
return new StringConcatenationInFormatCallFix(referenceName);
}
private static class StringConcatenationInFormatCallFix extends InspectionGadgetsFix {
private final String variableName;
public StringConcatenationInFormatCallFix(String variableName) {
this.variableName = variableName;
}
@Override
@NotNull
public String getName() {
return InspectionGadgetsBundle.message("string.concatenation.in.format.call.quickfix", variableName);
}
@NotNull
@Override
public String getFamilyName() {
return "Replace concatenation with argument";
}
@Override
protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
final PsiElement element = descriptor.getPsiElement();
if (!(element instanceof PsiBinaryExpression)) {
return;
}
final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)element;
final PsiElement parent = binaryExpression.getParent();
if (!(parent instanceof PsiExpressionList)) {
return;
}
final PsiExpressionList expressionList = (PsiExpressionList)parent;
final PsiExpression lhs = binaryExpression.getLOperand();
final PsiExpression rhs = binaryExpression.getROperand();
if (rhs == null) {
return;
}
final PsiExpression[] expressions = expressionList.getExpressions();
final int parameter = expressions.length - 1;
expressionList.add(rhs);
final Object constant =
ExpressionUtils.computeConstantExpression(lhs);
if (constant instanceof String) {
final PsiExpression newExpression = addParameter(lhs, parameter);
if (newExpression == null) {
expressionList.addAfter(lhs, binaryExpression);
}
else {
expressionList.addAfter(newExpression, binaryExpression);
}
}
else {
expressionList.addAfter(lhs, binaryExpression);
}
binaryExpression.delete();
}
@Nullable
private static PsiExpression addParameter(PsiExpression expression, int parameterNumber) {
if (expression instanceof PsiBinaryExpression) {
final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)expression;
final PsiExpression rhs = binaryExpression.getROperand();
if (rhs == null) {
return null;
}
final PsiExpression newExpression = addParameter(rhs, parameterNumber);
if (newExpression == null) {
return null;
}
rhs.replace(newExpression);
return expression;
}
else if (expression instanceof PsiLiteralExpression) {
final PsiLiteralExpression literalExpression = (PsiLiteralExpression)expression;
final Object value = literalExpression.getValue();
if (!(value instanceof String)) {
return null;
}
final Project project = expression.getProject();
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
return factory.createExpressionFromText("\"" + value + '{' + parameterNumber + "}\"", null);
}
else {
return null;
}
}
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new StringConcatenationInMessageFormatCallVisitor();
}
private static class StringConcatenationInMessageFormatCallVisitor extends BaseInspectionVisitor {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
if (!isMessageFormatCall(expression)) {
return;
}
final PsiExpressionList argumentList = expression.getArgumentList();
final PsiExpression[] arguments = argumentList.getExpressions();
if (arguments.length == 0) {
return;
}
final PsiExpression firstArgument = arguments[0];
final PsiType type = firstArgument.getType();
if (type == null) {
return;
}
final int formatArgumentIndex;
if ("java.util.Locale".equals(type.getCanonicalText()) && arguments.length > 1) {
formatArgumentIndex = 1;
}
else {
formatArgumentIndex = 0;
}
final PsiExpression formatArgument = arguments[formatArgumentIndex];
final PsiType formatArgumentType = formatArgument.getType();
if (formatArgumentType == null || !formatArgumentType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
return;
}
if (!(formatArgument instanceof PsiBinaryExpression)) {
return;
}
if (PsiUtil.isConstantExpression(formatArgument)) {
return;
}
final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)formatArgument;
final PsiExpression lhs = binaryExpression.getLOperand();
final PsiType lhsType = lhs.getType();
if (lhsType == null || !lhsType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
return;
}
final PsiExpression rhs = binaryExpression.getROperand();
if (!(rhs instanceof PsiReferenceExpression)) {
return;
}
registerError(formatArgument, rhs);
}
private static boolean isMessageFormatCall(PsiMethodCallExpression expression) {
final PsiReferenceExpression methodExpression = expression.getMethodExpression();
@NonNls final String referenceName = methodExpression.getReferenceName();
if (!"format".equals(referenceName)) {
return false;
}
final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
if (!(qualifierExpression instanceof PsiReferenceExpression)) {
return false;
}
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifierExpression;
final PsiElement target = referenceExpression.resolve();
if (!(target instanceof PsiClass)) {
return false;
}
final PsiClass aClass = (PsiClass)target;
return InheritanceUtil.isInheritor(aClass, "java.text.MessageFormat");
}
}
}