blob: 6fa8eb70165fe39f6aad114df7bb485de817fdfe [file] [log] [blame]
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* 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 org.jetbrains.idea.devkit.inspections;
import com.intellij.codeInspection.*;
import com.intellij.lang.properties.psi.Property;
import com.intellij.lang.properties.references.PropertyReference;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author yole
*/
public class TitleCapitalizationInspection extends BaseJavaLocalInspectionTool {
@Override
public boolean isEnabledByDefault() {
return true;
}
@Nls
@NotNull
@Override
public String getGroupDisplayName() {
return "Plugin DevKit";
}
@Nls
@NotNull
@Override
public String getDisplayName() {
return "Incorrect dialog title capitalization";
}
@NotNull
@Override
public String getShortName() {
return "DialogTitleCapitalization";
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
PsiReferenceExpression methodExpression = expression.getMethodExpression();
String calledName = methodExpression.getReferenceName();
if (calledName == null) return;
if ("setTitle".equals(calledName)) {
if (!isMethodOfClass(expression, DialogWrapper.class.getName(), FileChooserDescriptor.class.getName())) return;
PsiExpression[] args = expression.getArgumentList().getExpressions();
if (args.length == 0) {
return;
}
String titleValue = getTitleValue(args [0]);
if (!hasTitleCapitalization(titleValue)) {
holder.registerProblem(args [0], "Dialog title '" + titleValue + "' is not properly capitalized. It should have title capitalization",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new TitleCapitalizationFix(titleValue));
}
}
else if (calledName.startsWith("show") && (calledName.endsWith("Dialog") || calledName.endsWith("Message"))) {
if (!isMethodOfClass(expression, Messages.class.getName())) return;
PsiExpression[] args = expression.getArgumentList().getExpressions();
PsiMethod psiMethod = expression.resolveMethod();
assert psiMethod != null;
PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
for (int i = 0, parametersLength = parameters.length; i < parametersLength; i++) {
PsiParameter parameter = parameters[i];
if ("title".equals(parameter.getName()) && i < args.length) {
String titleValue = getTitleValue(args [i]);
if (!hasTitleCapitalization(titleValue)) {
holder.registerProblem(args [i], "Message title '" + titleValue + "' is not properly capitalized. It should have title capitalization",
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new TitleCapitalizationFix(titleValue));
}
break;
}
}
}
}
};
}
private static boolean isMethodOfClass(PsiMethodCallExpression expression, String... classNames) {
PsiMethod psiMethod = expression.resolveMethod();
if (psiMethod == null) {
return false;
}
PsiClass containingClass = psiMethod.getContainingClass();
if (containingClass == null) {
return false;
}
String name = containingClass.getQualifiedName();
return ArrayUtil.contains(name, classNames);
}
@Nullable
private static String getTitleValue(PsiExpression arg) {
if (arg instanceof PsiLiteralExpression) {
Object value = ((PsiLiteralExpression)arg).getValue();
if (value instanceof String) {
return (String) value;
}
}
if (arg instanceof PsiMethodCallExpression) {
PsiMethod psiMethod = ((PsiMethodCallExpression)arg).resolveMethod();
PsiExpression returnValue = PropertyUtil.getGetterReturnExpression(psiMethod);
if (returnValue != null) {
return getTitleValue(returnValue);
}
Property propertyArgument = getPropertyArgument((PsiMethodCallExpression)arg);
if (propertyArgument != null) {
return propertyArgument.getUnescapedValue();
}
}
if (arg instanceof PsiReferenceExpression) {
PsiElement result = ((PsiReferenceExpression)arg).resolve();
if (result instanceof PsiVariable && ((PsiVariable)result).hasModifierProperty(PsiModifier.FINAL)) {
return getTitleValue(((PsiVariable) result).getInitializer());
}
}
return null;
}
@Nullable
private static Property getPropertyArgument(PsiMethodCallExpression arg) {
PsiExpression[] args = arg.getArgumentList().getExpressions();
if (args.length > 0) {
PsiReference[] references = args[0].getReferences();
for (PsiReference reference : references) {
if (reference instanceof PropertyReference) {
ResolveResult[] resolveResults = ((PropertyReference)reference).multiResolve(false);
if (resolveResults.length == 1 && resolveResults[0].isValidResult()) {
PsiElement element = resolveResults[0].getElement();
if (element instanceof Property) {
return (Property) element;
}
}
}
}
}
return null;
}
private static boolean hasTitleCapitalization(String value) {
if (value == null) {
return true;
}
value = value.replace("&", "");
return StringUtil.wordsToBeginFromUpperCase(value).equals(value);
}
private static class TitleCapitalizationFix implements LocalQuickFix {
private final String myTitleValue;
public TitleCapitalizationFix(String titleValue) {
myTitleValue = titleValue;
}
@NotNull
@Override
public String getName() {
return "Properly capitalize '" + myTitleValue + '\'';
}
public final void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement problemElement = descriptor.getPsiElement();
if (problemElement == null || !problemElement.isValid()) {
return;
}
if (isQuickFixOnReadOnlyFile(problemElement)) {
return;
}
try {
doFix(project, problemElement);
}
catch (IncorrectOperationException e) {
final Class<? extends TitleCapitalizationFix> aClass = getClass();
final String className = aClass.getName();
final Logger logger = Logger.getInstance(className);
logger.error(e);
}
}
protected void doFix(Project project, PsiElement element) throws IncorrectOperationException {
if (element instanceof PsiLiteralExpression) {
final PsiLiteralExpression literalExpression = (PsiLiteralExpression)element;
final Object value = literalExpression.getValue();
if (!(value instanceof String)) {
return;
}
final String string = (String)value;
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
final PsiExpression
newExpression = factory.createExpressionFromText('"' + StringUtil.wordsToBeginFromUpperCase(string) + '"', element);
literalExpression.replace(newExpression);
}else if (element instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)element;
final PsiMethod method = methodCallExpression.resolveMethod();
final PsiExpression returnValue = PropertyUtil.getGetterReturnExpression(method);
if (returnValue != null) {
doFix(project, returnValue);
}
final Property property = getPropertyArgument(methodCallExpression);
if (property == null) {
return;
}
final String value = property.getUnescapedValue();
if (value == null) {
return;
}
final String capitalizedString = StringUtil.wordsToBeginFromUpperCase(value);
property.setValue(capitalizedString);
} else if (element instanceof PsiReferenceExpression) {
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)element;
final PsiElement target = referenceExpression.resolve();
if (!(target instanceof PsiVariable)) {
return;
}
final PsiVariable variable = (PsiVariable)target;
if (variable.hasModifierProperty(PsiModifier.FINAL)) {
doFix(project, variable.getInitializer());
}
}
}
protected static boolean isQuickFixOnReadOnlyFile(PsiElement problemElement) {
final PsiFile containingPsiFile = problemElement.getContainingFile();
if (containingPsiFile == null) {
return false;
}
final VirtualFile virtualFile = containingPsiFile.getVirtualFile();
if (virtualFile == null) {
return false;
}
final Project project = problemElement.getProject();
final ReadonlyStatusHandler handler = ReadonlyStatusHandler.getInstance(project);
final ReadonlyStatusHandler.OperationStatus status = handler.ensureFilesWritable(virtualFile);
return status.hasReadonlyFiles();
}
@NotNull
@Override
public String getFamilyName() {
return "Properly capitalize";
}
}
}