blob: 411ee1abde9842ec667fe97f4781a07254d4d35e [file] [log] [blame]
/*
* Copyright 2000-2012 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.
*/
/**
* @author cdr
*/
package com.intellij.codeInspection.i18n;
import com.intellij.ExtensionPoints;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.intention.AddAnnotationFix;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ex.BaseLocalInspectionTool;
import com.intellij.ide.util.TreeClassChooser;
import com.intellij.ide.util.TreeClassChooserFactory;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.introduceField.IntroduceConstantHandler;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.ui.AddDeleteListPanel;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.FieldPanel;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class I18nInspection extends BaseLocalInspectionTool {
public boolean ignoreForAssertStatements = true;
public boolean ignoreForExceptionConstructors = true;
@NonNls public String ignoreForSpecifiedExceptionConstructors = "";
public boolean ignoreForJUnitAsserts = true;
public boolean ignoreForClassReferences = true;
public boolean ignoreForPropertyKeyReferences = true;
public boolean ignoreForNonAlpha = true;
public boolean ignoreAssignedToConstants = false;
public boolean ignoreToString = false;
@NonNls public String nonNlsCommentPattern = "NON-NLS";
private boolean ignoreForEnumConstants = false;
private static final LocalQuickFix I18N_QUICK_FIX = new I18nizeQuickFix();
private static final I18nizeConcatenationQuickFix I18N_CONCATENATION_QUICK_FIX = new I18nizeConcatenationQuickFix();
@Nullable private Pattern myCachedNonNlsPattern;
@NonNls private static final String TO_STRING = "toString";
public I18nInspection() {
cacheNonNlsCommentPattern();
}
@Override
public SuppressIntentionAction[] getSuppressActions(PsiElement element) {
SuppressIntentionAction[] actions = {};
if (myCachedNonNlsPattern != null) {
actions = new SuppressIntentionAction[]{new SuppressByCommentOutAction(nonNlsCommentPattern)};
}
return ArrayUtil.mergeArrays(actions, super.getSuppressActions(element));
}
private static final String SKIP_FOR_ENUM = "ignoreForEnumConstant";
@Override
public void writeSettings(@NotNull Element node) throws WriteExternalException {
super.writeSettings(node);
if (ignoreForEnumConstants) {
final Element e = new Element("option");
e.setAttribute("name", SKIP_FOR_ENUM);
e.setAttribute("value", Boolean.toString(ignoreForEnumConstants));
node.addContent(e);
}
}
@Override
public void readSettings(@NotNull Element node) throws InvalidDataException {
super.readSettings(node);
for (Object o : node.getChildren()) {
if (o instanceof Element && Comparing.strEqual(node.getAttributeValue("name"), SKIP_FOR_ENUM)) {
final String ignoreForConstantsAttr = node.getAttributeValue("value");
if (ignoreForConstantsAttr != null) {
ignoreForEnumConstants = Boolean.parseBoolean(ignoreForConstantsAttr);
}
break;
}
}
cacheNonNlsCommentPattern();
}
@Override
@NotNull
public String getGroupDisplayName() {
return GroupNames.INTERNATIONALIZATION_GROUP_NAME;
}
@Override
@NotNull
public String getDisplayName() {
return CodeInsightBundle.message("inspection.i18n.display.name");
}
@Override
@NotNull
public String getShortName() {
return "HardCodedStringLiteral";
}
@Override
public JComponent createOptionsPanel() {
final GridBagLayout layout = new GridBagLayout();
final JPanel panel = new JPanel(layout);
final JCheckBox assertStatementsCheckbox = new JCheckBox(CodeInsightBundle.message("inspection.i18n.option.ignore.assert"), ignoreForAssertStatements);
assertStatementsCheckbox.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ignoreForAssertStatements = assertStatementsCheckbox.isSelected();
}
});
final JCheckBox exceptionConstructorCheck =
new JCheckBox(CodeInsightBundle.message("inspection.i18n.option.ignore.for.exception.constructor.arguments"),
ignoreForExceptionConstructors);
exceptionConstructorCheck.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ignoreForExceptionConstructors = exceptionConstructorCheck.isSelected();
}
});
final JTextField specifiedExceptions = new JTextField(ignoreForSpecifiedExceptionConstructors);
specifiedExceptions.getDocument().addDocumentListener(new DocumentAdapter(){
@Override
protected void textChanged(DocumentEvent e) {
ignoreForSpecifiedExceptionConstructors = specifiedExceptions.getText();
}
});
final JCheckBox junitAssertCheckbox = new JCheckBox(
CodeInsightBundle.message("inspection.i18n.option.ignore.for.junit.assert.arguments"), ignoreForJUnitAsserts);
junitAssertCheckbox.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ignoreForJUnitAsserts = junitAssertCheckbox.isSelected();
}
});
final JCheckBox classRef = new JCheckBox(CodeInsightBundle.message("inspection.i18n.option.ignore.qualified.class.names"), ignoreForClassReferences);
classRef.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ignoreForClassReferences = classRef.isSelected();
}
});
final JCheckBox propertyRef = new JCheckBox(CodeInsightBundle.message("inspection.i18n.option.ignore.property.keys"), ignoreForPropertyKeyReferences);
propertyRef.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ignoreForPropertyKeyReferences = propertyRef.isSelected();
}
});
final JCheckBox nonAlpha = new JCheckBox(CodeInsightBundle.message("inspection.i18n.option.ignore.nonalphanumerics"), ignoreForNonAlpha);
nonAlpha.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ignoreForNonAlpha = nonAlpha.isSelected();
}
});
final JCheckBox assignedToConstants = new JCheckBox(CodeInsightBundle.message("inspection.i18n.option.ignore.assigned.to.constants"), ignoreAssignedToConstants);
assignedToConstants.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ignoreAssignedToConstants = assignedToConstants.isSelected();
}
});
final JCheckBox chkToString = new JCheckBox(CodeInsightBundle.message("inspection.i18n.option.ignore.tostring"), ignoreToString);
chkToString.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ignoreToString = chkToString.isSelected();
}
});
final JCheckBox ignoreEnumConstants = new JCheckBox("Ignore enum constants", ignoreForEnumConstants);
ignoreEnumConstants.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
ignoreForEnumConstants = ignoreEnumConstants.isSelected();
}
});
final GridBagConstraints gc = new GridBagConstraints();
gc.fill = GridBagConstraints.HORIZONTAL;
gc.insets.bottom = 2;
gc.gridx = GridBagConstraints.REMAINDER;
gc.gridy = 0;
gc.weightx = 1;
gc.weighty = 0;
panel.add(assertStatementsCheckbox, gc);
gc.gridy ++;
panel.add(junitAssertCheckbox, gc);
gc.gridy ++;
panel.add(exceptionConstructorCheck, gc);
gc.gridy ++;
final Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
panel.add(new FieldPanel(specifiedExceptions,
null,
CodeInsightBundle.message("inspection.i18n.option.ignore.for.specified.exception.constructor.arguments"),
openProjects.length == 0 ? null :
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
createIgnoreExceptionsConfigurationDialog(openProjects[0], specifiedExceptions).show();
}
},
null), gc);
gc.gridy ++;
panel.add(classRef, gc);
gc.gridy ++;
panel.add(propertyRef, gc);
gc.gridy++;
panel.add(assignedToConstants, gc);
gc.gridy++;
panel.add(chkToString, gc);
gc.gridy ++;
panel.add(nonAlpha, gc);
gc.gridy ++;
panel.add(ignoreEnumConstants, gc);
gc.gridy ++;
gc.anchor = GridBagConstraints.NORTHWEST;
gc.weighty = 1;
final JTextField text = new JTextField(nonNlsCommentPattern);
final FieldPanel nonNlsCommentPatternComponent =
new FieldPanel(text, CodeInsightBundle.message("inspection.i18n.option.ignore.comment.pattern"),
CodeInsightBundle.message("inspection.i18n.option.ignore.comment.title"), null, new Runnable() {
@Override
public void run() {
nonNlsCommentPattern = text.getText();
cacheNonNlsCommentPattern();
}
});
panel.add(nonNlsCommentPatternComponent, gc);
final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(panel);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setBorder(null);
scrollPane.setPreferredSize(new Dimension(panel.getPreferredSize().width + scrollPane.getVerticalScrollBar().getPreferredSize().width,
panel.getPreferredSize().height +
scrollPane.getHorizontalScrollBar().getPreferredSize().height));
return scrollPane;
}
@SuppressWarnings({"NonStaticInitializer"})
private DialogWrapper createIgnoreExceptionsConfigurationDialog(final Project project, final JTextField specifiedExceptions) {
return new DialogWrapper(true) {
private AddDeleteListPanel myPanel;
{
setTitle(CodeInsightBundle.message(
"inspection.i18n.option.ignore.for.specified.exception.constructor.arguments"));
init();
}
@Override
protected JComponent createCenterPanel() {
final String[] ignored = ignoreForSpecifiedExceptionConstructors.split(",");
final List<String> initialList = new ArrayList<String>();
if (ignored != null){
for (String e : ignored) {
if (e.length() > 0) initialList.add(e);
}
}
myPanel = new AddDeleteListPanel<String>(null, initialList) {
@Override
protected String findItemToAdd() {
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
TreeClassChooser chooser = TreeClassChooserFactory.getInstance(project).
createInheritanceClassChooser(
CodeInsightBundle.message("inspection.i18n.option.ignore.for.specified.exception.constructor.arguments"), scope,
JavaPsiFacade.getInstance(project).findClass("java.lang.Throwable", scope), true, true, null);
chooser.showDialog();
PsiClass selectedClass = chooser.getSelected();
return selectedClass != null ? selectedClass.getQualifiedName() : null;
}
};
return myPanel;
}
@Override
protected void doOKAction() {
StringBuilder buf = new StringBuilder();
final Object[] exceptions = myPanel.getListItems();
for (Object exception : exceptions) {
buf.append(",").append(exception);
}
specifiedExceptions.setText(buf.length() > 0 ? buf.substring(1) : buf.toString());
super.doOKAction();
}
};
}
@Override
@Nullable
public ProblemDescriptor[] checkMethod(@NotNull PsiMethod method, @NotNull InspectionManager manager, boolean isOnTheFly) {
PsiClass containingClass = method.getContainingClass();
if (containingClass == null || isClassNonNls(containingClass)) {
return null;
}
final PsiCodeBlock body = method.getBody();
if (body != null) {
return checkElement(body, manager, isOnTheFly);
}
return null;
}
@Override
@Nullable
public ProblemDescriptor[] checkClass(@NotNull PsiClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
if (isClassNonNls(aClass)) {
return null;
}
final PsiClassInitializer[] initializers = aClass.getInitializers();
List<ProblemDescriptor> result = new ArrayList<ProblemDescriptor>();
for (PsiClassInitializer initializer : initializers) {
final ProblemDescriptor[] descriptors = checkElement(initializer, manager, isOnTheFly);
if (descriptors != null) {
ContainerUtil.addAll(result, descriptors);
}
}
return result.isEmpty() ? null : result.toArray(new ProblemDescriptor[result.size()]);
}
@Override
@Nullable
public ProblemDescriptor[] checkField(@NotNull PsiField field, @NotNull InspectionManager manager, boolean isOnTheFly) {
PsiClass containingClass = field.getContainingClass();
if (containingClass == null || isClassNonNls(containingClass)) {
return null;
}
if (AnnotationUtil.isAnnotated(field, AnnotationUtil.NON_NLS, false, false)) {
return null;
}
final PsiExpression initializer = field.getInitializer();
if (initializer != null) return checkElement(initializer, manager, isOnTheFly);
if (field instanceof PsiEnumConstant) {
return checkElement(((PsiEnumConstant)field).getArgumentList(), manager, isOnTheFly);
}
return null;
}
@Override
@Nullable
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
ExtensionPoint<FileCheckingInspection> point = Extensions.getRootArea().getExtensionPoint(ExtensionPoints.I18N_INSPECTION_TOOL);
final FileCheckingInspection[] fileCheckingInspections = point.getExtensions();
for(FileCheckingInspection obj: fileCheckingInspections) {
ProblemDescriptor[] descriptors = obj.checkFile(file, manager, isOnTheFly);
if (descriptors != null) {
return descriptors;
}
}
return null;
}
private ProblemDescriptor[] checkElement(final PsiElement element, InspectionManager manager, boolean isOnTheFly) {
StringI18nVisitor visitor = new StringI18nVisitor(manager, isOnTheFly);
element.accept(visitor);
List<ProblemDescriptor> problems = visitor.getProblems();
return problems.isEmpty() ? null : problems.toArray(new ProblemDescriptor[problems.size()]);
}
private static LocalQuickFix createIntroduceConstantFix() {
return new LocalQuickFix() {
@Override
@NotNull
public String getName() {
return IntroduceConstantHandler.REFACTORING_NAME;
}
@Override
public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
//do it later because it is invoked from write action
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
PsiElement element = descriptor.getPsiElement();
if (!(element instanceof PsiExpression)) return;
PsiExpression[] expressions = {(PsiExpression)element};
new IntroduceConstantHandler().invoke(project, expressions);
}
}, project.getDisposed());
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
};
}
private class StringI18nVisitor extends JavaRecursiveElementVisitor {
private final List<ProblemDescriptor> myProblems = new ArrayList<ProblemDescriptor>();
private final InspectionManager myManager;
private final boolean myOnTheFly;
public StringI18nVisitor(final InspectionManager manager, boolean onTheFly) {
myManager = manager;
myOnTheFly = onTheFly;
}
@Override public void visitAnonymousClass(PsiAnonymousClass aClass) {
final PsiExpressionList argumentList = aClass.getArgumentList();
if (argumentList != null) {
argumentList.accept(this);
}
}
@Override public void visitClass(PsiClass aClass) {}
@Override public void visitField(PsiField field) {}
@Override public void visitMethod(PsiMethod method) {}
@Override public void visitLiteralExpression(PsiLiteralExpression expression) {
Object value = expression.getValue();
if (!(value instanceof String)) return;
String stringValue = (String)value;
if (stringValue.trim().length() == 0) {
return;
}
Set<PsiModifierListOwner> nonNlsTargets = new THashSet<PsiModifierListOwner>();
if (canBeI18ned(myManager.getProject(), expression, stringValue, nonNlsTargets)) {
PsiField parentField = PsiTreeUtil.getParentOfType(expression, PsiField.class);
if (parentField != null) {
nonNlsTargets.add(parentField);
}
final String description = CodeInsightBundle.message("inspection.i18n.message.general.with.value", "#ref");
List<LocalQuickFix> fixes = new ArrayList<LocalQuickFix>();
if (I18nizeConcatenationQuickFix.getEnclosingLiteralConcatenation(expression) != null) {
fixes.add(I18N_CONCATENATION_QUICK_FIX);
}
fixes.add(I18N_QUICK_FIX);
if (!isNotConstantFieldInitializer(expression)) {
fixes.add(createIntroduceConstantFix());
}
final Project project = expression.getManager().getProject();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
if (PsiUtil.isLanguageLevel5OrHigher(expression)) {
for (PsiModifierListOwner element : nonNlsTargets) {
if (!AnnotationUtil.isAnnotated(element, AnnotationUtil.NLS, true, false)) {
if (!element.getManager().isInProject(element) || facade.findClass(AnnotationUtil.NON_NLS, element.getResolveScope()) != null) {
fixes.add(new AddAnnotationFix(AnnotationUtil.NON_NLS, element));
}
}
}
}
LocalQuickFix[] farr = fixes.toArray(new LocalQuickFix[fixes.size()]);
final ProblemDescriptor problem = myManager.createProblemDescriptor(expression,
description, myOnTheFly, farr,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
myProblems.add(problem);
}
}
private boolean isNotConstantFieldInitializer(final PsiExpression expression) {
PsiField parentField = expression.getParent() instanceof PsiField ? (PsiField) expression.getParent() : null;
return parentField != null && expression == parentField.getInitializer() &&
parentField.hasModifierProperty(PsiModifier.FINAL) &&
parentField.hasModifierProperty(PsiModifier.STATIC);
}
@Override public void visitAnnotation(PsiAnnotation annotation) {
//prevent from @SuppressWarnings
if (!BatchSuppressManager.SUPPRESS_INSPECTIONS_ANNOTATION_NAME.equals(annotation.getQualifiedName())){
super.visitAnnotation(annotation);
}
}
public List<ProblemDescriptor> getProblems() {
return myProblems;
}
}
private boolean canBeI18ned(@NotNull Project project, @NotNull PsiLiteralExpression expression, @NotNull String value, @NotNull Set<PsiModifierListOwner> nonNlsTargets) {
if (ignoreForNonAlpha && !StringUtil.containsAlphaCharacters(value)) {
return false;
}
if (JavaI18nUtil.isPassedToAnnotatedParam(project, expression, AnnotationUtil.NON_NLS, new HashMap<String, Object>(), nonNlsTargets)) {
return false;
}
if (isInNonNlsCall(project, expression, nonNlsTargets)) {
return false;
}
if (isInNonNlsEquals(expression, nonNlsTargets)) {
return false;
}
if (isPassedToNonNlsVariable(project, expression, nonNlsTargets)) {
return false;
}
if (JavaI18nUtil.mustBePropertyKey(project, expression, new HashMap<String, Object>())) {
return false;
}
if (isReturnedFromNonNlsMethod(expression, nonNlsTargets)) {
return false;
}
if (ignoreForAssertStatements && isArgOfAssertStatement(expression)) {
return false;
}
if (ignoreForExceptionConstructors && isArgOfExceptionConstructor(expression)) {
return false;
}
if (ignoreForEnumConstants && isArgOfEnumConstant(expression)) {
return false;
}
if (!ignoreForExceptionConstructors && isArgOfSpecifiedExceptionConstructor(expression, ignoreForSpecifiedExceptionConstructors.split(","))) {
return false;
}
if (ignoreForJUnitAsserts && isArgOfJUnitAssertion(expression)) {
return false;
}
if (ignoreForClassReferences && isClassRef(expression, value)) {
return false;
}
if (ignoreForPropertyKeyReferences && JavaI18nUtil.isPropertyRef(expression, value, null)) {
return false;
}
if (ignoreToString && isToString(expression)) {
return false;
}
Pattern pattern = myCachedNonNlsPattern;
if (pattern != null) {
PsiFile file = expression.getContainingFile();
Document document = PsiDocumentManager.getInstance(project).getDocument(file);
int line = document.getLineNumber(expression.getTextRange().getStartOffset());
int lineStartOffset = document.getLineStartOffset(line);
CharSequence lineText = document.getCharsSequence().subSequence(lineStartOffset, document.getLineEndOffset(line));
Matcher matcher = pattern.matcher(lineText);
int start = 0;
while (matcher.find(start)) {
start = matcher.start();
PsiElement element = file.findElementAt(lineStartOffset + start);
if (PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null) return false;
if (start == lineText.length() - 1) break;
start++;
}
}
return true;
}
private boolean isArgOfEnumConstant(PsiLiteralExpression expression) {
final PsiElement parent = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, PsiClass.class);
if (!(parent instanceof PsiExpressionList)) {
return false;
}
final PsiElement grandparent = parent.getParent();
return grandparent instanceof PsiEnumConstant;
}
public void cacheNonNlsCommentPattern() {
myCachedNonNlsPattern = nonNlsCommentPattern.trim().length() == 0 ? null : Pattern.compile(nonNlsCommentPattern);
}
private static boolean isClassRef(final PsiLiteralExpression expression, String value) {
if (StringUtil.startsWithChar(value,'#')) {
value = value.substring(1); // A favor for JetBrains team to catch common Logger usage practice.
}
return JavaPsiFacade.getInstance(expression.getProject()).findClass(value, GlobalSearchScope.allScope(expression.getProject())) != null;
}
@Override
public boolean isEnabledByDefault() {
return false;
}
private static boolean isClassNonNls(@NotNull PsiClass clazz) {
final PsiDirectory directory = clazz.getContainingFile().getContainingDirectory();
return directory != null && isPackageNonNls(JavaDirectoryService.getInstance().getPackage(directory));
}
public static boolean isPackageNonNls(final PsiPackage psiPackage) {
if (psiPackage == null || psiPackage.getName() == null) {
return false;
}
final PsiModifierList pkgModifierList = psiPackage.getAnnotationList();
return pkgModifierList != null && pkgModifierList.findAnnotation(AnnotationUtil.NON_NLS) != null
|| isPackageNonNls(psiPackage.getParentPackage());
}
private boolean isPassedToNonNlsVariable(@NotNull Project project,
@NotNull PsiLiteralExpression expression,
final Set<PsiModifierListOwner> nonNlsTargets) {
PsiExpression toplevel = JavaI18nUtil.getToplevelExpression(project, expression);
PsiVariable var = null;
if (toplevel instanceof PsiAssignmentExpression) {
PsiExpression lExpression = ((PsiAssignmentExpression)toplevel).getLExpression();
while (lExpression instanceof PsiArrayAccessExpression) {
lExpression = ((PsiArrayAccessExpression)lExpression).getArrayExpression();
}
if (lExpression instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)lExpression).resolve();
if (resolved instanceof PsiVariable) var = (PsiVariable)resolved;
}
}
if (var == null) {
PsiElement parent = toplevel.getParent();
if (parent instanceof PsiVariable && toplevel.equals(((PsiVariable)parent).getInitializer())) {
var = (PsiVariable)parent;
} else if (parent instanceof PsiSwitchLabelStatement) {
final PsiSwitchStatement switchStatement = ((PsiSwitchLabelStatement)parent).getEnclosingSwitchStatement();
if (switchStatement != null) {
final PsiExpression switchStatementExpression = switchStatement.getExpression();
if (switchStatementExpression instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)switchStatementExpression).resolve();
if (resolved instanceof PsiVariable) var = (PsiVariable)resolved;
}
}
}
}
if (var != null) {
if (annotatedAsNonNls(var)) {
return true;
}
if (ignoreAssignedToConstants &&
var.hasModifierProperty(PsiModifier.STATIC) &&
var.hasModifierProperty(PsiModifier.FINAL)) {
return true;
}
nonNlsTargets.add(var);
}
return false;
}
private static boolean annotatedAsNonNls(final PsiModifierListOwner parent) {
if (parent instanceof PsiParameter) {
final PsiParameter parameter = (PsiParameter)parent;
final PsiElement declarationScope = parameter.getDeclarationScope();
if (declarationScope instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)declarationScope;
final int index = method.getParameterList().getParameterIndex(parameter);
return JavaI18nUtil.isMethodParameterAnnotatedWith(method, index, null, AnnotationUtil.NON_NLS, null, null);
}
}
return AnnotationUtil.isAnnotated(parent, AnnotationUtil.NON_NLS, false, false);
}
private static boolean isInNonNlsEquals(PsiExpression expression, final Set<PsiModifierListOwner> nonNlsTargets) {
if (!(expression.getParent().getParent() instanceof PsiMethodCallExpression)) {
return false;
}
final PsiMethodCallExpression call = (PsiMethodCallExpression)expression.getParent().getParent();
final PsiReferenceExpression methodExpression = call.getMethodExpression();
final PsiExpression qualifier = methodExpression.getQualifierExpression();
if (qualifier != expression) {
return false;
}
if (!"equals".equals(methodExpression.getReferenceName())) {
return false;
}
final PsiElement resolved = methodExpression.resolve();
if (!(resolved instanceof PsiMethod)) {
return false;
}
PsiType objectType = PsiType.getJavaLangObject(resolved.getManager(), resolved.getResolveScope());
MethodSignature equalsSignature = MethodSignatureUtil.createMethodSignature("equals",
new PsiType[]{objectType},
PsiTypeParameter.EMPTY_ARRAY,
PsiSubstitutor.EMPTY);
if (!equalsSignature.equals(((PsiMethod)resolved).getSignature(PsiSubstitutor.EMPTY))) {
return false;
}
final PsiExpression[] expressions = call.getArgumentList().getExpressions();
if (expressions.length != 1) {
return false;
}
final PsiExpression arg = expressions[0];
PsiReferenceExpression ref = null;
if (arg instanceof PsiReferenceExpression) {
ref = (PsiReferenceExpression)arg;
}
else if (arg instanceof PsiMethodCallExpression) ref = ((PsiMethodCallExpression)arg).getMethodExpression();
if (ref != null) {
final PsiElement resolvedEntity = ref.resolve();
if (resolvedEntity instanceof PsiModifierListOwner) {
PsiModifierListOwner modifierListOwner = (PsiModifierListOwner)resolvedEntity;
if (annotatedAsNonNls(modifierListOwner)) {
return true;
}
nonNlsTargets.add(modifierListOwner);
}
}
return false;
}
private static boolean isInNonNlsCall(@NotNull Project project, @NotNull PsiExpression expression, final Set<PsiModifierListOwner> nonNlsTargets) {
expression = JavaI18nUtil.getToplevelExpression(project, expression);
final PsiElement parent = expression.getParent();
if (parent instanceof PsiExpressionList) {
final PsiElement grParent = parent.getParent();
if (grParent instanceof PsiMethodCallExpression) {
return isNonNlsCall((PsiMethodCallExpression)grParent, nonNlsTargets);
}
else if (grParent instanceof PsiNewExpression) {
final PsiElement parentOfNew = grParent.getParent();
if (parentOfNew instanceof PsiLocalVariable) {
final PsiLocalVariable newVariable = (PsiLocalVariable)parentOfNew;
if (annotatedAsNonNls(newVariable)) {
return true;
}
nonNlsTargets.add(newVariable);
return false;
}
else if (parentOfNew instanceof PsiAssignmentExpression) {
final PsiExpression lExpression = ((PsiAssignmentExpression)parentOfNew).getLExpression();
if (lExpression instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)lExpression).resolve();
if (resolved instanceof PsiModifierListOwner) {
final PsiModifierListOwner modifierListOwner = (PsiModifierListOwner)resolved;
if (annotatedAsNonNls(modifierListOwner)) {
return true;
}
nonNlsTargets.add(modifierListOwner);
return false;
}
}
}
}
}
return false;
}
private static boolean isNonNlsCall(PsiMethodCallExpression grParent, Set<PsiModifierListOwner> nonNlsTargets) {
final PsiReferenceExpression methodExpression = grParent.getMethodExpression();
final PsiExpression qualifier = methodExpression.getQualifierExpression();
if (qualifier instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)qualifier).resolve();
if (resolved instanceof PsiModifierListOwner) {
final PsiModifierListOwner modifierListOwner = (PsiModifierListOwner)resolved;
if (annotatedAsNonNls(modifierListOwner)) {
return true;
}
nonNlsTargets.add(modifierListOwner);
return false;
}
} else if (qualifier instanceof PsiMethodCallExpression) {
final PsiType type = qualifier.getType();
if (type != null && type.equals(methodExpression.getType())) {
return isNonNlsCall((PsiMethodCallExpression)qualifier, nonNlsTargets);
}
}
return false;
}
private static boolean isReturnedFromNonNlsMethod(final PsiLiteralExpression expression, final Set<PsiModifierListOwner> nonNlsTargets) {
PsiElement parent = expression.getParent();
PsiMethod method;
if (parent instanceof PsiNameValuePair) {
method = AnnotationUtil.getAnnotationMethod((PsiNameValuePair)parent);
}
else {
final PsiElement returnStmt = PsiTreeUtil.getParentOfType(expression, PsiReturnStatement.class, PsiMethodCallExpression.class);
if (returnStmt == null || !(returnStmt instanceof PsiReturnStatement)) {
return false;
}
method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class);
}
if (method == null) return false;
if (AnnotationUtil.isAnnotated(method, AnnotationUtil.NON_NLS, true, false)) {
return true;
}
nonNlsTargets.add(method);
return false;
}
private static boolean isToString(final PsiLiteralExpression expression) {
final PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class);
if (method == null) return false;
final PsiType returnType = method.getReturnType();
return TO_STRING.equals(method.getName())
&& method.getParameterList().getParametersCount() == 0
&& returnType != null
&& "java.lang.String".equals(returnType.getCanonicalText());
}
private static boolean isArgOfJUnitAssertion(PsiExpression expression) {
final PsiElement parent = expression.getParent();
if (!(parent instanceof PsiExpressionList)) {
return false;
}
final PsiElement grandparent = parent.getParent();
if (!(grandparent instanceof PsiMethodCallExpression)) {
return false;
}
final PsiMethodCallExpression call = (PsiMethodCallExpression)grandparent;
final PsiReferenceExpression methodExpression = call.getMethodExpression();
@NonNls final String methodName = methodExpression.getReferenceName();
if (methodName == null) {
return false;
}
if (!methodName.startsWith("assert") && !methodName.equals("fail")) {
return false;
}
final PsiMethod method = call.resolveMethod();
if (method == null) {
return false;
}
final PsiClass containingClass = method.getContainingClass();
if (containingClass == null) {
return false;
}
final Project project = expression.getProject();
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
final PsiClass junitAssert = JavaPsiFacade.getInstance(project).findClass("junit.framework.Assert", scope);
return junitAssert != null && !containingClass.isInheritor(junitAssert, true);
}
private static boolean isArgOfExceptionConstructor(PsiExpression expression) {
final PsiElement parent = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, PsiClass.class);
if (!(parent instanceof PsiExpressionList)) {
return false;
}
final PsiElement grandparent = parent.getParent();
final PsiClass aClass;
if (RefactoringChangeUtil.isSuperOrThisMethodCall(grandparent)) {
final PsiMethod method = ((PsiMethodCallExpression)grandparent).resolveMethod();
if (method != null) {
aClass = method.getContainingClass();
} else {
return false;
}
} else {
if (!(grandparent instanceof PsiNewExpression)) {
return false;
}
final PsiJavaCodeReferenceElement reference = ((PsiNewExpression)grandparent).getClassReference();
if (reference == null) {
return false;
}
final PsiElement referent = reference.resolve();
if (!(referent instanceof PsiClass)) {
return false;
}
aClass = (PsiClass)referent;
}
final Project project = expression.getProject();
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
final PsiClass throwable = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_THROWABLE, scope);
return throwable != null && aClass.isInheritor(throwable, true);
}
private static boolean isArgOfSpecifiedExceptionConstructor(PsiExpression expression, String[] specifiedExceptions) {
if (specifiedExceptions.length == 0) return false;
final PsiElement parent = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, PsiClass.class);
if (!(parent instanceof PsiExpressionList)) {
return false;
}
final PsiElement grandparent = parent.getParent();
if (!(grandparent instanceof PsiNewExpression)) {
return false;
}
final PsiJavaCodeReferenceElement reference =
((PsiNewExpression)grandparent).getClassReference();
if (reference == null) {
return false;
}
final PsiElement referent = reference.resolve();
if (!(referent instanceof PsiClass)) {
return false;
}
final PsiClass aClass = (PsiClass)referent;
for (String specifiedException : specifiedExceptions) {
if (specifiedException.equals(aClass.getQualifiedName())) return true;
}
return false;
}
private static boolean isArgOfAssertStatement(PsiExpression expression) {
return PsiTreeUtil.getParentOfType(expression, PsiAssertStatement.class, PsiClass.class) instanceof PsiAssertStatement;
}
}