checkers) {
+ for (ConditionChecker checker : checkers) {
+ if (checker.matchesPsiMethod(psiMethod, paramIndex))
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java b/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java
new file mode 100644
index 000000000000..de3871d0e61b
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInsight/ConditionChecker.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright 2000-2013 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 com.intellij.codeInsight;
+
+import com.intellij.openapi.diagnostic.*;
+import com.intellij.psi.*;
+import org.jetbrains.annotations.*;
+
+import java.io.*;
+import java.util.*;
+
+
+/**
+ * Used by Constant Condition Inspection to identify methods which perform some type of Validation on the parameters passed into them.
+ * For example given the following method
+ *
+ * {@code
+ * class Foo {
+ * static boolean validateNotNull(Object o) {
+ * if (o == null) return false;
+ * else return true;
+ * }
+ * }
+ * }
+ *
+ * The corresponding ConditionCheck would be
+ * myConditionCheckType=Type.IS_NOT_NULL_METHOD
+ * myClassName=Foo
+ * myMethodName=validateNotNull
+ * myPsiParameter=o
+ *
+ * The following block of code would produce a Inspection Warning that o is always true
+ *
+ *
+ * {@code
+ * if (Value.isNotNull(o)) {
+ * if(o != null) {}
+ * }
+ * }
+ *
+ *
+ * @author Johnny Clark
+ * Creation Date: 8/14/12
+ */
+public class ConditionChecker implements Serializable {
+ private final @NotNull Type myConditionCheckType;
+
+ public enum Type {
+ IS_NULL_METHOD("IsNull Method"),
+ IS_NOT_NULL_METHOD("IsNotNull Method"),
+ ASSERT_IS_NULL_METHOD("Assert IsNull Method"),
+ ASSERT_IS_NOT_NULL_METHOD("Assert IsNotNull Method"),
+ ASSERT_TRUE_METHOD("Assert True Method"),
+ ASSERT_FALSE_METHOD("Assert False Method");
+ private final String myStringRepresentation;
+
+ Type(String stringRepresentation) {
+ myStringRepresentation = stringRepresentation;
+ }
+
+ @Override
+ public String toString() {
+ return myStringRepresentation;
+ }
+ }
+
+ private final @NotNull String myClassName;
+ private final @NotNull String myMethodName;
+ private final @NotNull List myParameterClassList;
+ private final int myCheckedParameterIndex;
+ private final String myFullName;
+
+ private ConditionChecker(@NotNull String className,
+ @NotNull String methodName,
+ @NotNull List parameterClassList,
+ int checkedParameterIndex,
+ @NotNull Type type,
+ @NotNull String fullName) {
+ checkState(!className.isEmpty(), "Class Name is blank");
+ checkState(!methodName.isEmpty(), "Method Name is blank");
+ checkState(!parameterClassList.isEmpty(), "Parameter Class List is empty");
+ checkState(checkedParameterIndex >= 0, "CheckedParameterIndex must be greater than or equal to zero");
+ checkState(parameterClassList.size() >= checkedParameterIndex, "CheckedParameterIndex is greater than Parameter Class List's size");
+ checkState(!fullName.isEmpty(), "Method Name is blank");
+
+ myConditionCheckType = type;
+ myClassName = className;
+ myMethodName = methodName;
+ myParameterClassList = parameterClassList;
+ myCheckedParameterIndex = checkedParameterIndex;
+ myFullName = fullName;
+ }
+
+ private static void checkState(boolean condition, String errorMsg) {
+ if (!condition) throw new IllegalArgumentException(errorMsg);
+ }
+
+ public static String getFullyQualifiedName(PsiParameter psiParameter) {
+ PsiTypeElement typeElement = psiParameter.getTypeElement();
+ if (typeElement == null) throw new RuntimeException("Parameter has null typeElement " + psiParameter.getName());
+
+ PsiType psiType = typeElement.getType();
+
+ return psiType.getCanonicalText();
+ }
+
+ public boolean matchesPsiMethod(PsiMethod psiMethod) {
+ if (!myMethodName.equals(psiMethod.getName())) return false;
+
+ PsiClass containingClass = psiMethod.getContainingClass();
+ if (containingClass == null) return false;
+
+ String qualifiedName = containingClass.getQualifiedName();
+ if (qualifiedName == null) return false;
+
+ if (!myClassName.equals(qualifiedName)) return false;
+
+ PsiParameterList psiParameterList = psiMethod.getParameterList();
+ if (myParameterClassList.size() != psiParameterList.getParameters().length) return false;
+
+ for (int i = 0; i < psiParameterList.getParameters().length; i++) {
+ PsiParameter psiParameter = psiParameterList.getParameters()[i];
+ PsiTypeElement psiTypeElement = psiParameter.getTypeElement();
+ if (psiTypeElement == null) return false;
+
+ PsiType psiType = psiTypeElement.getType();
+ String parameterCanonicalText = psiType.getCanonicalText();
+ String myParameterCanonicalText = myParameterClassList.get(i);
+ if (!myParameterCanonicalText.equals(parameterCanonicalText)) return false;
+ }
+
+ return true;
+ }
+
+ public boolean matchesPsiMethod(PsiMethod psiMethod, int paramIndex) {
+ if (matchesPsiMethod(psiMethod) && paramIndex == myCheckedParameterIndex) return true;
+
+ return false;
+ }
+
+ public boolean overlaps(ConditionChecker otherChecker) {
+ if (myClassName.equals(otherChecker.myClassName) &&
+ myMethodName.equals(otherChecker.myMethodName) &&
+ myParameterClassList.equals(otherChecker.myParameterClassList) &&
+ myCheckedParameterIndex == otherChecker.myCheckedParameterIndex) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @NotNull
+ public Type getConditionCheckType() {
+ return myConditionCheckType;
+ }
+
+ @NotNull
+ public String getClassName() {
+ return myClassName;
+ }
+
+ @NotNull
+ public String getMethodName() {
+ return myMethodName;
+ }
+
+ public int getCheckedParameterIndex() {
+ return myCheckedParameterIndex;
+ }
+
+ public String getFullName() {
+ return myFullName;
+ }
+
+ /**
+ * In addition to normal duties, this controls the manner in which the ConditionCheck appears in the ConditionCheckDialog.MethodsPanel
+ */
+ @Override
+ public String toString() {
+ return myFullName;
+ }
+
+ private static class Builder {
+
+ static String initFullName(String className,
+ String methodName,
+ List parameterClasses,
+ List parameterNames,
+ int checkedParameterIndex) {
+ String s = className + "." + methodName + "(";
+ int index = 0;
+ for (String parameterClass : parameterClasses) {
+ String parameterClassAndName = parameterClass + " " + parameterNames.get(index);
+ if (index == checkedParameterIndex) parameterClassAndName = "*" + parameterClassAndName + "*";
+
+ s += parameterClassAndName + ", ";
+ index++;
+ }
+ s = s.substring(0, s.length() - 2);
+ s += ")";
+ return s;
+ }
+ }
+
+ static class FromConfigBuilder extends Builder {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ConditionCheck.FromConfigBuilder");
+ private final @NotNull String serializedRepresentation;
+ private final @NotNull Type type;
+
+ FromConfigBuilder(@NotNull String serializedRepresentation, @NotNull Type type) {
+ this.serializedRepresentation = serializedRepresentation;
+ this.type = type;
+ }
+
+ private String parseClassAndMethodName() {
+ if (!serializedRepresentation.contains("(")) {
+ throw new IllegalArgumentException("Name should contain a opening parenthesis. " + serializedRepresentation);
+ }
+ else if (!serializedRepresentation.contains(")")) {
+ throw new IllegalArgumentException("Name should contain a closing parenthesis. " + serializedRepresentation);
+ }
+ else if (serializedRepresentation.indexOf("(", serializedRepresentation.indexOf("(") + 1) > -1) {
+ throw new IllegalArgumentException("Name should only contain one opening parenthesis. " + serializedRepresentation);
+ }
+ else if (serializedRepresentation.indexOf(")", serializedRepresentation.indexOf(")") + 1) > -1) {
+ throw new IllegalArgumentException("Name should only contain one closing parenthesis. " + serializedRepresentation);
+ }
+ else if (serializedRepresentation.indexOf(")") < serializedRepresentation.indexOf("(")) {
+ throw new IllegalArgumentException("Opening parenthesis should precede closing parenthesis. " + serializedRepresentation);
+ }
+
+ String classAndMethodName = serializedRepresentation.substring(0, serializedRepresentation.indexOf("("));
+ if (!classAndMethodName.contains(".")) {
+ throw new IllegalArgumentException(
+ "Name should contain a dot between the class name and method name (before the opening parenthesis). " +
+ serializedRepresentation);
+ }
+ return classAndMethodName;
+ }
+
+ @Nullable
+ public ConditionChecker build() {
+ try {
+ String classAndMethodName = parseClassAndMethodName();
+
+ String className = classAndMethodName.substring(0, classAndMethodName.lastIndexOf("."));
+ String methodName = classAndMethodName.substring(classAndMethodName.lastIndexOf(".") + 1);
+
+ String allParametersSubString =
+ serializedRepresentation.substring(serializedRepresentation.indexOf("(") + 1, serializedRepresentation.lastIndexOf(")")).trim();
+ if (allParametersSubString.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Name should contain 1+ parameter (between opening and closing parenthesis). " + serializedRepresentation);
+ }
+ else if (allParametersSubString.contains("*") && allParametersSubString.indexOf("*") == allParametersSubString.lastIndexOf("*")) {
+ throw new IllegalArgumentException("Selected Parameter should be surrounded by asterisks. " + serializedRepresentation);
+ }
+
+ List parameterClasses = new ArrayList();
+ List parameterNames = new ArrayList();
+ int checkParameterIndex = -1;
+ int index = 0;
+ for (String parameterClassAndName : allParametersSubString.split(",")) {
+ parameterClassAndName = parameterClassAndName.trim();
+ if (parameterClassAndName.startsWith("*") && parameterClassAndName.endsWith("*")) {
+ checkParameterIndex = index;
+ parameterClassAndName = parameterClassAndName.substring(1, parameterClassAndName.length() - 1);
+ }
+
+ String[] parameterClassAndNameSplit = parameterClassAndName.split(" ");
+ String parameterClass = parameterClassAndNameSplit[0];
+ String parameterName = parameterClassAndNameSplit[1];
+ parameterClasses.add(parameterClass);
+ parameterNames.add(parameterName);
+ index++;
+ }
+ String fullName = initFullName(className, methodName, parameterClasses, parameterNames, checkParameterIndex);
+ return new ConditionChecker(className, methodName, parameterClasses, checkParameterIndex, type, fullName);
+ }
+ catch (Exception e) {
+ LOG.error("An Exception occurred while attempting to build ConditionCheck for Serialized String '" +
+ serializedRepresentation +
+ "' and Type '" +
+ type +
+ "'", e);
+ return null;
+ }
+ }
+ }
+
+ public static class FromPsiBuilder extends Builder {
+ private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ConditionCheck.FromPsiBuilder");
+ private final @NotNull PsiMethod psiMethod;
+ private final @NotNull PsiParameter psiParameter;
+ private final @NotNull Type type;
+
+ public FromPsiBuilder(@NotNull PsiMethod psiMethod, @NotNull PsiParameter psiParameter, @NotNull Type type) {
+ this.psiMethod = psiMethod;
+ this.psiParameter = psiParameter;
+ this.type = type;
+ }
+
+ private static void validatePsiMethodHasContainingClass(PsiMethod psiMethod) {
+ PsiElement psiElement = psiMethod.getContainingClass();
+ if (!(psiElement instanceof PsiClass)) {
+ throw new IllegalArgumentException("PsiMethod " + psiMethod + " can not have a null containing class.");
+ }
+ }
+
+ private static void validatePsiMethodReturnTypeForNonAsserts(PsiMethod psiMethod, Type type) {
+ PsiType returnType = psiMethod.getReturnType();
+ if (isAssert(type)) return;
+
+ if (returnType == null) throw new IllegalArgumentException("PsiMethod " + psiMethod + " has a null return type PsiType.");
+
+ if (returnType != PsiType.BOOLEAN && !returnType.getCanonicalText().equals(Boolean.class.toString())) {
+ throw new IllegalArgumentException("PsiMethod " + psiMethod + " must have a null return type PsiType of boolean or Boolean.");
+ }
+ }
+
+ private static void validatePsiParameterExistsInPsiMethod(PsiMethod psiMethod, PsiParameter psiParameter) {
+ boolean parameterFound = false;
+ PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+ for (PsiParameter parameter : parameters) {
+ if (psiParameter.equals(parameter)) {
+ parameterFound = true;
+ break;
+ }
+ }
+
+ if (!parameterFound) {
+ throw new IllegalArgumentException("PsiMethod " + psiMethod + " must have parameter " + getFullyQualifiedName(psiParameter));
+ }
+ }
+
+ private static boolean isAssert(Type type) {
+ return type == Type.ASSERT_IS_NULL_METHOD ||
+ type == Type.ASSERT_IS_NOT_NULL_METHOD ||
+ type == Type.ASSERT_TRUE_METHOD ||
+ type == Type.ASSERT_FALSE_METHOD;
+ }
+
+ private static String initClassNameFromPsiMethod(PsiMethod psiMethod) {
+ PsiElement psiElement = psiMethod.getContainingClass();
+ PsiClass psiClass = (PsiClass)psiElement;
+ if (psiClass == null) throw new IllegalStateException("PsiClass is null");
+
+ String qualifiedName = psiClass.getQualifiedName();
+ if (qualifiedName == null || qualifiedName.isEmpty()) throw new IllegalStateException("Qualified Name is Blank");
+ return qualifiedName;
+ }
+
+ private static String initMethodNameFromPsiMethod(PsiMethod psiMethod) {
+ return psiMethod.getName();
+ }
+
+ private static List initParameterClassListFromPsiMethod(PsiMethod psiMethod) {
+ List parameterClasses = new ArrayList();
+ PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+ for (PsiParameter param : parameters) {
+ PsiTypeElement typeElement = param.getTypeElement();
+ if (typeElement == null) throw new RuntimeException("Parameter has null typeElement " + param.getName());
+
+ PsiType psiType = typeElement.getType();
+
+ parameterClasses.add(psiType.getCanonicalText());
+ }
+ return parameterClasses;
+ }
+
+ private static List initParameterNameListFromPsiMethod(PsiMethod psiMethod) {
+ List parameterNames = new ArrayList();
+ PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+ for (PsiParameter param : parameters) {
+ parameterNames.add(param.getName());
+ }
+ return parameterNames;
+ }
+
+ private static int initCheckedParameterIndex(PsiMethod psiMethod, PsiParameter psiParameterToFind) {
+ PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+ for (int i = 0; i < parameters.length; i++) {
+ PsiParameter param = parameters[i];
+ if (param.equals(psiParameterToFind)) return i;
+ }
+ throw new IllegalStateException();
+ }
+
+ private void validateConstructorArgs(PsiMethod psiMethod, PsiParameter psiParameter) {
+ validatePsiMethodHasContainingClass(psiMethod);
+ validatePsiMethodReturnTypeForNonAsserts(psiMethod, type);
+ validatePsiParameterExistsInPsiMethod(psiMethod, psiParameter);
+ }
+
+ @Nullable
+ public ConditionChecker build() {
+ try {
+ validateConstructorArgs(psiMethod, psiParameter);
+
+ String className = initClassNameFromPsiMethod(psiMethod);
+ String methodName = initMethodNameFromPsiMethod(psiMethod);
+ List parameterClassList = initParameterClassListFromPsiMethod(psiMethod);
+ List parameterNameList = initParameterNameListFromPsiMethod(psiMethod);
+ int checkedParameterIndex = initCheckedParameterIndex(psiMethod, psiParameter);
+ String fullName = initFullName(className, methodName, parameterClassList, parameterNameList, checkedParameterIndex);
+ return new ConditionChecker(className, methodName, parameterClassList, checkedParameterIndex, type, fullName);
+ }
+ catch (Exception e) {
+ LOG.error("An Exception occurred while attempting to build ConditionCheck for PsiMethod '" + psiMethod +
+ "' PsiParameter='" + psiParameter + "' " +
+ "' and Type '" +
+ type +
+ "'", e);
+ return null;
+ }
+ }
+ }
+}
diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ConditionCheckDialog.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ConditionCheckDialog.java
new file mode 100644
index 000000000000..0f8a7257efdf
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ConditionCheckDialog.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2000-2013 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 com.intellij.codeInspection.dataFlow;
+
+import com.intellij.codeInsight.*;
+import com.intellij.codeInspection.*;
+import com.intellij.openapi.project.*;
+import com.intellij.openapi.ui.*;
+import com.intellij.ui.*;
+import com.intellij.ui.components.*;
+import org.jetbrains.annotations.*;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * Dialog that appears when user clicks the "Configure IsNull/IsNotNull/True/False Check/Assertion Methods"
+ * on the Errors dialog for the Constant Conditions Inspection. It is divided into 6 parts
+ *
+ * - Is Null Check MethodsPanel
+ * - Is Not Null Check MethodsPanel
+ * - Assert Is Null MethodsPanel
+ * - Assert Is Not Null MethodsPanel
+ * - Assert True MethodsPanel
+ * - Assert False MethodsPanel
+ *
+ *
+ * @author Johnny Clark
+ * Creation Date: 8/3/12
+ */
+public class ConditionCheckDialog extends DialogWrapper {
+ private final Project myProject;
+ private final @NotNull Splitter mainSplitter;
+ private final @NotNull MethodsPanel myIsNullCheckMethodPanel;
+ private final @NotNull MethodsPanel myIsNotNullCheckMethodPanel;
+ private final @NotNull MethodsPanel myAssertIsNullMethodPanel;
+ private final @NotNull MethodsPanel myAssertIsNotNullMethodPanel;
+ private final @NotNull MethodsPanel myAssertTrueMethodPanel;
+ private final @NotNull MethodsPanel myAssertFalseMethodPanel;
+
+ public ConditionCheckDialog(Project project, String mainDialogTitle) {
+ super(project, true);
+ myProject = project;
+
+ final ConditionCheckManager manager = ConditionCheckManager.getInstance(myProject);
+ mainSplitter = new Splitter(true, 0.3f);
+ final Splitter topThirdSplitter = new Splitter(false);
+ final Splitter bottomTwoThirdsSplitter = new Splitter(true);
+ final Splitter isNullIsNotNullCheckMethodSplitter = new Splitter(false);
+ final Splitter assertTrueFalseMethodSplitter = new Splitter(false);
+
+ List isNullCheckMethods = new ArrayList(manager.getIsNullCheckMethods());
+ List isNotNullCheckMethods = new ArrayList(manager.getIsNotNullCheckMethods());
+ List assertIsNullMethods = new ArrayList(manager.getAssertIsNullMethods());
+ List assertIsNotNullMethods = new ArrayList(manager.getAssertIsNotNullMethods());
+ List assertTrueMethods = new ArrayList(manager.getAssertTrueMethods());
+ List assertFalseMethods = new ArrayList(manager.getAssertFalseMethods());
+
+ myAssertIsNullMethodPanel = new MethodsPanel(assertIsNullMethods, ConditionChecker.Type.ASSERT_IS_NULL_METHOD, myProject);
+ myAssertIsNotNullMethodPanel = new MethodsPanel(assertIsNotNullMethods, ConditionChecker.Type.ASSERT_IS_NOT_NULL_METHOD, myProject);
+ myIsNullCheckMethodPanel = new MethodsPanel(isNullCheckMethods, ConditionChecker.Type.IS_NULL_METHOD, myProject);
+ myIsNotNullCheckMethodPanel = new MethodsPanel(isNotNullCheckMethods, ConditionChecker.Type.IS_NOT_NULL_METHOD, myProject);
+ myAssertTrueMethodPanel = new MethodsPanel(assertTrueMethods, ConditionChecker.Type.ASSERT_TRUE_METHOD, myProject);
+ myAssertFalseMethodPanel = new MethodsPanel(assertFalseMethods, ConditionChecker.Type.ASSERT_FALSE_METHOD, myProject);
+
+ isNullIsNotNullCheckMethodSplitter.setFirstComponent(myIsNullCheckMethodPanel.getComponent());
+ isNullIsNotNullCheckMethodSplitter.setSecondComponent(myIsNotNullCheckMethodPanel.getComponent());
+ assertTrueFalseMethodSplitter.setFirstComponent(myAssertTrueMethodPanel.getComponent());
+ assertTrueFalseMethodSplitter.setSecondComponent(myAssertFalseMethodPanel.getComponent());
+
+ topThirdSplitter.setFirstComponent(myAssertIsNullMethodPanel.getComponent());
+ topThirdSplitter.setSecondComponent(myAssertIsNotNullMethodPanel.getComponent());
+ bottomTwoThirdsSplitter.setFirstComponent(isNullIsNotNullCheckMethodSplitter);
+ bottomTwoThirdsSplitter.setSecondComponent(assertTrueFalseMethodSplitter);
+
+ mainSplitter.setFirstComponent(topThirdSplitter);
+ mainSplitter.setSecondComponent(bottomTwoThirdsSplitter);
+
+ topThirdSplitter.setPreferredSize(new Dimension(600, 150));
+ bottomTwoThirdsSplitter.setPreferredSize(new Dimension(600, 300));
+
+ myAssertIsNullMethodPanel
+ .setOtherMethodsPanels(myAssertIsNotNullMethodPanel, myIsNullCheckMethodPanel, myIsNotNullCheckMethodPanel, myAssertTrueMethodPanel,
+ myAssertFalseMethodPanel);
+ myAssertIsNotNullMethodPanel
+ .setOtherMethodsPanels(myAssertIsNullMethodPanel, myIsNullCheckMethodPanel, myIsNotNullCheckMethodPanel, myAssertTrueMethodPanel,
+ myAssertFalseMethodPanel);
+ myIsNullCheckMethodPanel
+ .setOtherMethodsPanels(myAssertIsNullMethodPanel, myAssertIsNotNullMethodPanel, myIsNotNullCheckMethodPanel, myAssertTrueMethodPanel,
+ myAssertFalseMethodPanel);
+ myIsNotNullCheckMethodPanel
+ .setOtherMethodsPanels(myAssertIsNullMethodPanel, myAssertIsNotNullMethodPanel, myIsNullCheckMethodPanel, myAssertTrueMethodPanel,
+ myAssertFalseMethodPanel);
+ myAssertTrueMethodPanel
+ .setOtherMethodsPanels(myAssertIsNullMethodPanel, myAssertIsNotNullMethodPanel, myIsNotNullCheckMethodPanel, myIsNullCheckMethodPanel,
+ myAssertFalseMethodPanel);
+ myAssertFalseMethodPanel
+ .setOtherMethodsPanels(myAssertIsNullMethodPanel, myAssertIsNotNullMethodPanel, myIsNotNullCheckMethodPanel, myIsNullCheckMethodPanel,
+ myAssertTrueMethodPanel);
+
+ init();
+ setTitle(mainDialogTitle);
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ return mainSplitter;
+ }
+
+ @Override
+ protected void doOKAction() {
+ final ConditionCheckManager manager = ConditionCheckManager.getInstance(myProject);
+ manager.setIsNotNullCheckMethods(myIsNotNullCheckMethodPanel.getConditionChecker());
+ manager.setIsNullCheckMethods(myIsNullCheckMethodPanel.getConditionChecker());
+ manager.setAssertIsNotNullMethods(myAssertIsNotNullMethodPanel.getConditionChecker());
+ manager.setAssertIsNullMethods(myAssertIsNullMethodPanel.getConditionChecker());
+ manager.setAssertTrueMethods(myAssertTrueMethodPanel.getConditionChecker());
+ manager.setAssertFalseMethods(myAssertFalseMethodPanel.getConditionChecker());
+
+ super.doOKAction();
+ }
+
+ /**
+ * Is Null, Is Not Null, Assert True and Assert False Method Panel at the top of the main Dialog.
+ */
+ class MethodsPanel {
+ private final @NotNull JBList myList;
+ private final @NotNull JPanel myPanel;
+ private final @NotNull Project myProject;
+ private Set otherPanels;
+
+ public MethodsPanel(final List checkers, final ConditionChecker.Type type, final @NotNull Project myProject) {
+ this.myProject = myProject;
+ myList = new JBList(new CollectionListModel(checkers));
+ myPanel = new JPanel(new BorderLayout());
+ myPanel.setBorder(IdeBorderFactory.createTitledBorder(initTitle(type), false, new Insets(10, 0, 0, 0)));
+ myPanel.setPreferredSize(new Dimension(400, 150));
+
+ myList.setCellRenderer(new ColoredListCellRenderer() {
+ @Override
+ protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
+ String s = value.toString();
+ if (s.contains("*")) {
+ int indexOfAsterix1 = s.indexOf("*");
+ int indexOfAsterix2 = s.lastIndexOf("*");
+ if (indexOfAsterix1 >= 0 &&
+ indexOfAsterix1 < s.length() &&
+ indexOfAsterix2 >= 0 &&
+ indexOfAsterix2 < s.length() &&
+ indexOfAsterix1 < indexOfAsterix2) {
+ append(s.substring(0, indexOfAsterix1), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ append(s.substring(indexOfAsterix1 + 1, indexOfAsterix2), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
+ append(s.substring(indexOfAsterix2 + 1), SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ }
+ else {
+ append(s, SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ }
+ }
+ }
+ });
+
+ final ToolbarDecorator toolbarDecorator =
+ ToolbarDecorator.createDecorator(myList).disableUpDownActions().setAddAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton anActionButton) {
+ chooseMethod(null, type, myList.getModel().getSize());
+ }
+ }).setRemoveAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton anActionButton) {
+ CollectionListModel model = getCollectionListModel();
+ if (myList.getSelectedIndex() >= 0 && myList.getSelectedIndex() < model.getSize()) {
+ model.remove(myList.getSelectedIndex());
+ }
+ }
+ });
+
+ myList.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ int index = myList.locationToIndex(e.getPoint());
+ CollectionListModel model = getCollectionListModel();
+ if (index >= 0 && model.getSize() > index) {
+ chooseMethod(model.getElementAt(index), type, index);
+ }
+ }
+ }
+ });
+ final JPanel panel = toolbarDecorator.createPanel();
+ myPanel.add(panel);
+ myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ }
+
+ private String initTitle(@NotNull ConditionChecker.Type type) {
+ if (type.equals(ConditionChecker.Type.IS_NULL_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.isNull.method.panel.title");
+ }
+ else if (type.equals(ConditionChecker.Type.IS_NOT_NULL_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.isNotNull.method.panel.title");
+ }
+ else if (type.equals(ConditionChecker.Type.ASSERT_IS_NULL_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.assert.isNull.method.panel.title");
+ }
+ else if (type.equals(ConditionChecker.Type.ASSERT_IS_NOT_NULL_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.assert.isNotNull.method.panel.title");
+ }
+ else if (type.equals(ConditionChecker.Type.ASSERT_TRUE_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.assert.true.method.panel.title");
+ }
+ else if (type.equals(ConditionChecker.Type.ASSERT_FALSE_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.assert.false.method.panel.title");
+ }
+ else {
+ throw new IllegalArgumentException("MethodCheckerDetailsDialog does not support type " + type);
+ }
+ }
+
+ private void chooseMethod(@Nullable ConditionChecker checker, ConditionChecker.Type type, int index) {
+ MethodCheckerDetailsDialog pickMethodPanel =
+ new MethodCheckerDetailsDialog(checker, type, myProject, myPanel, getConditionCheckers(), getOtherCheckers());
+ pickMethodPanel.show();
+ ConditionChecker chk = pickMethodPanel.getConditionChecker();
+ if (chk != null) {
+ CollectionListModel model = getCollectionListModel();
+ if (model.getSize() <= index) {
+ model.add(chk);
+ }
+ else {
+ model.setElementAt(chk, index);
+ }
+ }
+ }
+
+ private CollectionListModel getCollectionListModel() {
+ //noinspection unchecked
+ return (CollectionListModel)myList.getModel();
+ }
+
+ @NotNull
+ public JPanel getComponent() {
+ return myPanel;
+ }
+
+ public List getConditionChecker() {
+ CollectionListModel model = getCollectionListModel();
+ return new ArrayList(model.getItems());
+ }
+
+ public Set getConditionCheckers() {
+ Set set = new HashSet();
+ set.addAll(getConditionChecker());
+ return set;
+ }
+
+ public void setOtherMethodsPanels(MethodsPanel p1, MethodsPanel p2, MethodsPanel p3, MethodsPanel p4, MethodsPanel p5) {
+ otherPanels = new HashSet();
+ otherPanels.add(p1);
+ otherPanels.add(p2);
+ otherPanels.add(p3);
+ otherPanels.add(p4);
+ otherPanels.add(p5);
+ }
+
+ public Set getOtherCheckers() {
+ Set otherCheckers = new HashSet();
+ for (MethodsPanel otherPanel : otherPanels) {
+ otherCheckers.addAll(otherPanel.getConditionCheckers());
+ }
+ return otherCheckers;
+ }
+ }
+}
diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java
index 4b99a0540ebb..05f75a3a8fcf 100644
--- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java
+++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java
@@ -15,6 +15,7 @@
*/
package com.intellij.codeInspection.dataFlow;
+import com.intellij.codeInsight.ConditionCheckManager;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInspection.dataFlow.instructions.*;
import com.intellij.codeInspection.dataFlow.value.*;
@@ -1343,6 +1344,82 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
}
}
+ if (ConditionCheckManager.isCheck(resolved)) {
+ if (ConditionCheckManager.isAssertIsNullCheckMethod(resolved)) {
+ int paramIndex = 0;
+ for (PsiExpression param : params) {
+ param.accept(this);
+ if (ConditionCheckManager.isAssertIsNullCheckMethod(resolved, paramIndex++)) {
+ addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
+ addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject()));
+ conditionalExit(exitPoint, false); // Exit if Equal NULL is True
+ } else {
+ addInstruction(new PopInstruction());
+ }
+ }
+ return true;
+ } else if (ConditionCheckManager.isAssertIsNotNullCheckMethod(resolved)) {
+ int paramIndex = 0;
+ for (PsiExpression param : params) {
+ param.accept(this);
+ if (ConditionCheckManager.isAssertIsNotNullCheckMethod(resolved, paramIndex++)) {
+ addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
+ addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject()));
+ conditionalExit(exitPoint, true); // Exit if NotEqual NULL is True
+ } else {
+ addInstruction(new PopInstruction());
+ }
+ }
+ return true;
+ } else if (ConditionCheckManager.isNullCheckMethod(resolved)) {
+ int paramIndex = 0;
+ for (PsiExpression param : params) {
+ param.accept(this);
+ if (ConditionCheckManager.isNullCheckMethod(resolved, paramIndex++)) {
+ addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
+ addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, expression.getProject()));
+ } else {
+ addInstruction(new PopInstruction());
+ }
+ }
+ return true;
+ } else if (ConditionCheckManager.isNotNullCheckMethod(resolved)) {
+ int paramIndex = 0;
+ for (PsiExpression param : params) {
+ param.accept(this);
+ if (ConditionCheckManager.isNotNullCheckMethod(resolved, paramIndex++)) {
+ addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
+ addInstruction(new BinopInstruction(JavaTokenType.NE, null, expression.getProject()));
+ } else {
+ addInstruction(new PopInstruction());
+ }
+ }
+ return true;
+ } else if (ConditionCheckManager.isAssertTrueCheckMethod(resolved)) {
+ int paramIndex = 0;
+ for (PsiExpression param : params) {
+ param.accept(this);
+ if (ConditionCheckManager.isAssertTrueCheckMethod(resolved, paramIndex++)) {
+ conditionalExit(exitPoint, false);
+ } else {
+ addInstruction(new PopInstruction());
+ }
+ }
+ return true;
+ } else if (ConditionCheckManager.isAssertFalseCheckMethod(resolved)) {
+ int paramIndex = 0;
+ for (PsiExpression param : params) {
+ param.accept(this);
+ if (ConditionCheckManager.isAssertFalseCheckMethod(resolved, paramIndex++)) {
+ conditionalExit(exitPoint, true);
+ } else {
+ addInstruction(new PopInstruction());
+ }
+ }
+ return true;
+ }
+ }
+
// Idea project only.
if (qualifierExpression != null) {
if (qualifierExpression.textMatches("LOG")) {
diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java
index 56b1d22041f4..2b7bd60bae48 100644
--- a/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java
+++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspection.java
@@ -587,6 +587,24 @@ public class DataFlowInspection extends BaseLocalInspectionTool {
gc.insets.bottom = 15;
add(configureAnnotations, gc);
+ final JButton configureCheckAnnotations = new JButton(InspectionsBundle.message("configure.checker.option.button"));
+ configureCheckAnnotations.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ Project project = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(OptionsPanel.this));
+ if (project == null) project = ProjectManager.getInstance().getDefaultProject();
+ final ConditionCheckDialog dialog = new ConditionCheckDialog(project,
+ InspectionsBundle.message("configure.checker.option.main.dialog.title")
+ );
+ dialog.show();
+ }
+ });
+ gc.gridy++;
+ gc.fill = GridBagConstraints.NONE;
+ gc.insets.left = 20;
+ gc.insets.bottom = 15;
+ add(configureCheckAnnotations, gc);
+
gc.fill = GridBagConstraints.HORIZONTAL;
gc.weighty = 1;
gc.insets.left = 0;
diff --git a/java/java-impl/src/com/intellij/codeInspection/dataFlow/MethodCheckerDetailsDialog.java b/java/java-impl/src/com/intellij/codeInspection/dataFlow/MethodCheckerDetailsDialog.java
new file mode 100644
index 000000000000..5d34364e6452
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInspection/dataFlow/MethodCheckerDetailsDialog.java
@@ -0,0 +1,708 @@
+/*
+ * Copyright 2000-2013 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 com.intellij.codeInspection.dataFlow;
+
+import com.intellij.codeInsight.*;
+import com.intellij.codeInspection.*;
+import com.intellij.ide.util.*;
+import com.intellij.openapi.editor.event.*;
+import com.intellij.openapi.project.*;
+import com.intellij.openapi.ui.*;
+import com.intellij.psi.*;
+import com.intellij.psi.search.*;
+import com.intellij.ui.*;
+import org.jetbrains.annotations.*;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.beans.*;
+import java.util.*;
+import java.util.List;
+
+import static com.intellij.codeInsight.ConditionChecker.Type.*;
+
+/**
+ * Dialog that appears when the user clicks the Add Button or double clicks a row item in a MethodsPanel. The MethodsPanel is accessed from the ConditionCheckDialog
+ */
+class MethodCheckerDetailsDialog extends DialogWrapper implements PropertyChangeListener, ItemListener {
+ private final @NotNull ConditionChecker.Type myType;
+ private final @NotNull Project myProject;
+ private final @NotNull ParameterDropDown parameterDropDown;
+ private final @NotNull MethodDropDown methodDropDown;
+ private final @NotNull ClassField classField;
+ private final @NotNull Set myOtherCheckers;
+ private final @Nullable ConditionChecker myPreviouslySelectedChecker;
+ /**
+ * Set by the OK and/or Cancel actions so that the caller can retrieve it via a call to getMethodIsNullIsNotNullChecker
+ */
+ private @Nullable ConditionChecker mySelectedChecker;
+
+ MethodCheckerDetailsDialog(@Nullable ConditionChecker previouslySelectedChecker,
+ @NotNull ConditionChecker.Type type,
+ @NotNull Project project,
+ @NotNull Component component,
+ @NotNull Set otherCheckersSameType,
+ @NotNull Set otherCheckers) {
+ super(component, true);
+ if (!isSupported(type)) throw new IllegalArgumentException("Type is invalid " + type);
+
+ myProject = project;
+ myType = type;
+ myOtherCheckers = new HashSet(otherCheckersSameType);
+ myOtherCheckers.addAll(otherCheckers);
+ myPreviouslySelectedChecker = previouslySelectedChecker;
+ if (myPreviouslySelectedChecker != null) myOtherCheckers.remove(myPreviouslySelectedChecker);
+
+ PsiClass psiClass = null;
+ PsiMethod psiMethod = null;
+ PsiParameter psiParameter = null;
+ if (previouslySelectedChecker != null) {
+ psiClass =
+ JavaPsiFacade.getInstance(myProject).findClass(previouslySelectedChecker.getClassName(), GlobalSearchScope.allScope(myProject));
+ if (psiClass != null) {
+ for (PsiMethod method : psiClass.findMethodsByName(previouslySelectedChecker.getMethodName(), true)) {
+ if (previouslySelectedChecker.equals(buildParameterClassListFromPsiMethod(method))) {
+ psiMethod = method;
+ break;
+ }
+ }
+ }
+
+ if (psiMethod != null) {
+ PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+ if (parameters.length - 1 >= previouslySelectedChecker.getCheckedParameterIndex()) {
+ psiParameter = parameters[previouslySelectedChecker.getCheckedParameterIndex()];
+ }
+ }
+ }
+
+ if (psiClass == null || psiMethod == null || psiParameter == null) {
+ psiClass = null;
+ psiMethod = null;
+ psiParameter = null;
+ }
+
+ classField = new ClassField(myProject, psiClass);
+ methodDropDown = new MethodDropDown(psiClass, psiMethod, myType, MethodDropDown.buildModel());
+ parameterDropDown = new ParameterDropDown(psiMethod, psiParameter, ParameterDropDown.buildModel(), myType);
+ classField.addPropertyChangeListener(methodDropDown);
+ classField.addPropertyChangeListener(parameterDropDown);
+ classField.addPropertyChangeListener(this);
+ methodDropDown.addItemListener(parameterDropDown);
+ methodDropDown.addItemListener(this);
+ parameterDropDown.addItemListener(this);
+ init();
+ checkOkActionEnable();
+ setTitle(initTitle(type));
+ }
+
+ private static boolean isSupported(ConditionChecker.Type type) {
+ return type == IS_NULL_METHOD || type == IS_NOT_NULL_METHOD ||
+ type == ASSERT_IS_NULL_METHOD || type == ASSERT_IS_NOT_NULL_METHOD ||
+ type == ASSERT_TRUE_METHOD || type == ASSERT_FALSE_METHOD;
+ }
+
+ private static List buildParameterClassListFromPsiMethod(PsiMethod psiMethod) {
+ List parameterClasses = new ArrayList();
+ PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+ for (PsiParameter param : parameters) {
+ PsiTypeElement typeElement = param.getTypeElement();
+ if (typeElement == null) return new ArrayList();
+
+ PsiType psiType = typeElement.getType();
+
+ parameterClasses.add(psiType.getCanonicalText());
+ }
+ return parameterClasses;
+ }
+
+ private static String initTitle(@NotNull ConditionChecker.Type type) {
+ if (type.equals(IS_NULL_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.isNull.add.method.checker.dialog.title");
+ }
+ else if (type.equals(IS_NOT_NULL_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.isNotNull.add.method.checker.dialog.title");
+ }
+ else if (type.equals(ASSERT_IS_NULL_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.assert.isNull.add.method.checker.dialog.title");
+ }
+ else if (type.equals(ASSERT_IS_NOT_NULL_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.assert.isNotNull.add.method.checker.dialog.title");
+ }
+ else if (type.equals(ASSERT_TRUE_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.assert.true.add.method.checker.dialog.title");
+ }
+ else if (type.equals(ASSERT_FALSE_METHOD)) {
+ return InspectionsBundle.message("configure.checker.option.assert.false.add.method.checker.dialog.title");
+ }
+ else {
+ throw new IllegalArgumentException("MethodCheckerDetailsDialog does not support type " + type);
+ }
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ final JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+
+ final LabeledComponent classComponent = new LabeledComponent();
+ final LabeledComponent methodComponent = new LabeledComponent();
+ final LabeledComponent parameterComponent = new LabeledComponent();
+
+ classComponent.setText("Class");
+ methodComponent.setText("Method");
+ parameterComponent.setText("Parameter");
+
+ classComponent.setComponent(classField);
+ methodComponent.setComponent(methodDropDown);
+ parameterComponent.setComponent(parameterDropDown);
+
+ panel.add(classComponent);
+ panel.add(methodComponent);
+ panel.add(parameterComponent);
+
+ return panel;
+ }
+
+ @Nullable
+ ConditionChecker getConditionChecker() {
+ return mySelectedChecker;
+ }
+
+ @Nullable
+ private ConditionChecker buildConditionChecker() {
+ PsiClass psiClass = classField.getPsiClass();
+ PsiMethod psiMethod = methodDropDown.getSelectedPsiMethod();
+ PsiParameter psiParameter = parameterDropDown.getSelectedPsiParameter();
+ if (psiClass != null && psiMethod != null && psiParameter != null) {
+ return new ConditionChecker.FromPsiBuilder(psiMethod, psiParameter, myType).build();
+ }
+ else {
+ return null;
+ }
+ }
+
+ private boolean overlaps(ConditionChecker thisChecker) {
+ for (ConditionChecker overlappingChecker : myOtherCheckers) {
+ if (thisChecker.overlaps(overlappingChecker)) {
+ Messages.showMessageDialog(myProject, InspectionsBundle.message("configure.checker.option.overlap.error.msg") +
+ " " +
+ overlappingChecker.getConditionCheckType() + " " + overlappingChecker.toString(),
+ InspectionsBundle.message("configure.checker.option.overlap.error.title"), Messages.getErrorIcon());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ checkOkActionEnable();
+ }
+
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ checkOkActionEnable();
+ }
+
+ private void checkOkActionEnable() {
+ if (classField.getPsiClass() == null ||
+ methodDropDown.getSelectedPsiMethod() == null ||
+ parameterDropDown.getSelectedPsiParameter() == null) {
+ setOKActionEnabled(false);
+ }
+ else {
+ setOKActionEnabled(true);
+ }
+ }
+
+ @Override
+ protected void doOKAction() {
+ ConditionChecker checker = buildConditionChecker();
+ if (checker != null && !overlaps(checker)) {
+ if (checker.equals(myPreviouslySelectedChecker)) {
+ mySelectedChecker = myPreviouslySelectedChecker;
+ }
+ else {
+ mySelectedChecker = checker;
+ }
+ super.doOKAction();
+ }
+ }
+
+ public boolean isOKActionEnabled() {
+ if (!myOKAction.isEnabled()) return false;
+ PsiClass psiClass = classField.getPsiClass();
+ PsiMethod psiMethod = methodDropDown.getSelectedPsiMethod();
+ PsiParameter psiParameter = parameterDropDown.getSelectedPsiParameter();
+ if (psiClass == null || psiMethod == null || psiParameter == null) {
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+ /**
+ * Input Text Field for Class Name
+ */
+ static class ClassField extends EditorTextFieldWithBrowseButton implements ActionListener, DocumentListener {
+ public static final String PROPERTY_PSICLASS = "ClassField.myPsiClass";
+ private final @NotNull Project myProject;
+ private @Nullable PsiClass myPsiClass;
+
+ public ClassField(@NotNull Project project, @Nullable PsiClass psiClass) {
+ super(project, true, buildVisibilityChecker());
+ myProject = project;
+ myPsiClass = psiClass;
+ setPreferredSize(new Dimension(500, (int)getPreferredSize().getHeight()));
+ if (myPsiClass != null) { //noinspection ConstantConditions
+ setText(myPsiClass.getQualifiedName());
+ }
+ addActionListener(this);
+ getChildComponent().addDocumentListener(this);
+ }
+
+ private static JavaCodeFragment.VisibilityChecker buildVisibilityChecker() {
+ return new JavaCodeFragment.VisibilityChecker() {
+ @Override
+ public Visibility isDeclarationVisible(PsiElement declaration, PsiElement place) {
+ return Visibility.VISIBLE;
+ }
+ };
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final TreeClassChooser chooser = TreeClassChooserFactory.getInstance(myProject)
+ .createNoInnerClassesScopeChooser("Choose Class", GlobalSearchScope.allScope(myProject), new ClassFilter() {
+ @Override
+ public boolean isAccepted(PsiClass aClass) {
+ return !aClass.isAnnotationType();
+ }
+ }, null);
+ chooser.showDialog();
+ PsiClass psiClass = chooser.getSelected();
+ if (psiClass != null) { //noinspection ConstantConditions
+ setText(chooser.getSelected().getQualifiedName());
+ }
+ }
+
+ @Nullable
+ public PsiClass getPsiClass() {
+ return myPsiClass;
+ }
+
+ @Override
+ public void beforeDocumentChange(DocumentEvent event) {
+ }
+
+ @Override
+ public void documentChanged(DocumentEvent event) {
+ String className = event.getDocument().getText();
+ PsiClass psiClass = null;
+ if (className != null) {
+ psiClass = JavaPsiFacade.getInstance(myProject).findClass(className, GlobalSearchScope.allScope(myProject));
+ }
+
+ if (psiClass != null && myPsiClass != null) {
+ if (!psiClass.equals(myPsiClass)) {
+ firePropertyChange(PROPERTY_PSICLASS, myPsiClass, psiClass);
+ myPsiClass = psiClass;
+ }
+ }
+ else if (psiClass != null) {
+ firePropertyChange(PROPERTY_PSICLASS, myPsiClass, psiClass);
+ myPsiClass = psiClass;
+ }
+ else if (myPsiClass != null) {
+ firePropertyChange(PROPERTY_PSICLASS, myPsiClass, psiClass);
+ myPsiClass = null;
+ }
+ }
+ }
+
+ /**
+ * Drop Down for picking Method Name
+ */
+ static class MethodDropDown extends JComboBox implements PropertyChangeListener {
+ private final @NotNull ConditionChecker.Type myType;
+ private final @NotNull SortedComboBoxModel myModel;
+ private @Nullable PsiClass myPsiClass;
+
+ MethodDropDown(@Nullable PsiClass psiClass,
+ @Nullable PsiMethod psiMethod,
+ @NotNull ConditionChecker.Type type,
+ @NotNull SortedComboBoxModel model) {
+ super(model);
+
+ if (!isSupported(type)) throw new IllegalArgumentException("Type is invalid " + type);
+
+ myPsiClass = psiClass;
+ myType = type;
+ myModel = model;
+ setEnabled(myPsiClass != null);
+ initValues();
+ if (psiMethod != null) {
+ for (Iterator iterator = myModel.iterator(); iterator.hasNext(); ) {
+ MethodWrapper methodWrapper = iterator.next();
+ if (methodWrapper.getPsiMethod().equals(psiMethod)) {
+ setSelectedItem(methodWrapper);
+ }
+ }
+ }
+ }
+
+ private static boolean isMethodFromJavaLangObject(PsiMethod method) {
+ if (method == null) return false;
+
+ PsiClass containingClass = method.getContainingClass();
+ if (containingClass == null) return false;
+ String name = containingClass.getQualifiedName();
+ if (name == null) return false;
+
+ if (CommonClassNames.JAVA_LANG_OBJECT.equals(name)) return true;
+
+ return false;
+ }
+
+ @NotNull
+ public static SortedComboBoxModel buildModel() {
+ return new SortedComboBoxModel(new Comparator() {
+ @Override
+ public int compare(MethodWrapper o1, MethodWrapper o2) {
+ return o1.compareTo(o2);
+ }
+ });
+ }
+
+ private void initValues() {
+ if (myPsiClass != null) {
+ myModel.clear();
+ myModel.setSelectedItem(null);
+ PsiMethod[] allMethods = myPsiClass.getAllMethods();
+ for (PsiMethod method : allMethods) {
+ MethodWrapper methodWrapper = new MethodWrapper(method);
+ if (qualifies(method) && !myModel.getItems().contains(methodWrapper)) myModel.add(methodWrapper);
+ }
+ }
+ }
+
+ public boolean qualifies(PsiMethod psiMethod) {
+ if (isMethodFromJavaLangObject(psiMethod)) {
+ return false;
+ }
+
+ final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+ if (parameters.length < 1) {
+ return false;
+ }
+
+ if (myType == IS_NULL_METHOD || myType == IS_NOT_NULL_METHOD) {
+ PsiType returnType = psiMethod.getReturnType();
+ if (returnType != PsiType.BOOLEAN && (returnType == null || !returnType.getCanonicalText().equals(Boolean.class.toString()))) {
+ return false;
+ }
+ }
+ else if (myType == ASSERT_TRUE_METHOD || myType == ASSERT_FALSE_METHOD) {
+ boolean booleanParamExists = false;
+ for (PsiParameter psiParameter : parameters) {
+ PsiType type = psiParameter.getType();
+ if (type.equals(PsiType.BOOLEAN) || type.getCanonicalText().equals(Boolean.class.toString())) {
+ booleanParamExists = true;
+ break;
+ }
+ }
+
+ if (!booleanParamExists) {
+ return false;
+ }
+ }
+ // Else it's ASSERT_IS_NULL_METHOD or ASSERT_IS_NOT_NULL_METHOD.
+ // In that case there is no additional validation
+
+ return true;
+ }
+
+ /**
+ * Called when ClassField is set and when user selects entry in the MethodDropDown
+ */
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals(ClassField.PROPERTY_PSICLASS)) {
+ if (evt.getNewValue() == null) {
+ clear();
+ }
+ else {
+ setEnabled(true);
+ if (myPsiClass == null || !myPsiClass.equals(evt.getNewValue())) { // ClassChanged so refresh list
+ myPsiClass = (PsiClass)evt.getNewValue();
+ initValues();
+ }
+ }
+ }
+ }
+
+ public void clear() {
+ myModel.clear();
+ myModel.setSelectedItem(null);
+ setEnabled(false);
+ myPsiClass = null;
+ }
+
+ @Nullable
+ public PsiMethod getSelectedPsiMethod() {
+ MethodWrapper methodWrapper = myModel.getSelectedItem();
+ if (methodWrapper == null) return null;
+
+ return methodWrapper.getPsiMethod();
+ }
+ }
+
+ /**
+ * Drop Down for picking Parameter Name
+ */
+ static class ParameterDropDown extends JComboBox implements PropertyChangeListener, ItemListener {
+ private final @NotNull SortedComboBoxModel myModel;
+ private final @NotNull ConditionChecker.Type myType;
+ private @Nullable PsiMethod myPsiMethod;
+
+ public ParameterDropDown(@Nullable PsiMethod psiMethod,
+ @Nullable PsiParameter psiParameter,
+ @NotNull SortedComboBoxModel model,
+ @NotNull ConditionChecker.Type type) {
+ super(model);
+
+ if (!isSupported(type)) throw new IllegalArgumentException("Type is invalid " + type);
+
+ myPsiMethod = psiMethod;
+ myModel = model;
+ myType = type;
+
+ if (myPsiMethod != null) {
+ setEnabled(true);
+ myModel.addAll(getParameterWrappers());
+ if (psiParameter != null) {
+ for (Iterator iterator = myModel.iterator(); iterator.hasNext(); ) {
+ ParameterWrapper wrapper = (ParameterWrapper)iterator.next();
+ if (wrapper.getPsiParameter().equals(psiParameter)) setSelectedItem(wrapper);
+ }
+ }
+ }
+ else {
+ setEnabled(false);
+ }
+ }
+
+ public static SortedComboBoxModel buildModel() {
+ return new SortedComboBoxModel(new Comparator() {
+ @Override
+ public int compare(ParameterWrapper o1, ParameterWrapper o2) {
+ return o1.compareTo(o2);
+ }
+ });
+ }
+
+ List getParameterWrappers() {
+ List wrappers = new ArrayList();
+ if (myPsiMethod != null) {
+ PsiParameterList parameterList = myPsiMethod.getParameterList();
+ for (int i = 0; i < parameterList.getParameters().length; i++) {
+ PsiParameter psiParameter = parameterList.getParameters()[i];
+ if (myType == ASSERT_TRUE_METHOD || myType == ASSERT_FALSE_METHOD) {
+ PsiType type = psiParameter.getType();
+ if (type.equals(PsiType.BOOLEAN) || type.getCanonicalText().equals(Boolean.class.toString())) {
+ wrappers.add(new ParameterWrapper(psiParameter, i));
+ }
+ }
+ else {
+ wrappers.add(new ParameterWrapper(psiParameter, i));
+ }
+
+ }
+ }
+ return wrappers;
+ }
+
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getSource() instanceof MethodDropDown) { // The MethodDropDown has changed.
+ MethodDropDown methodDropDown = (MethodDropDown)e.getSource();
+ if (methodDropDown.getSelectedPsiMethod() != null) {
+ setEnabled(true);
+ if (myPsiMethod == null || !myPsiMethod.equals(methodDropDown.getSelectedPsiMethod())) {
+ myPsiMethod = methodDropDown.getSelectedPsiMethod();
+ myModel.clear();
+ myModel.addAll(getParameterWrappers());
+ myModel.setSelectedItem(null);
+ }
+ }
+ else {
+ myPsiMethod = null;
+ myModel.clear();
+ myModel.setSelectedItem(null);
+ setEnabled(false);
+ }
+ }
+ else {
+ throw new RuntimeException("Unexpected Configuration ParameterDropDown is only expected to receive events from MethodDropDown.");
+ }
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals(ClassField.PROPERTY_PSICLASS)) {
+ if (evt.getNewValue() == null) {
+ setEnabled(false);
+ }
+ }
+ }
+
+ @Nullable
+ public PsiParameter getSelectedPsiParameter() {
+ ParameterWrapper parameterWrapper = myModel.getSelectedItem();
+ if (parameterWrapper == null) return null;
+
+ return parameterWrapper.getPsiParameter();
+ }
+
+ class ParameterWrapper implements Comparable {
+ private final @NotNull String id;
+ private final @NotNull PsiParameter psiParameter;
+ private final int index;
+
+ ParameterWrapper(@NotNull PsiParameter psiParameter, int index) {
+ this.psiParameter = psiParameter;
+ this.index = index;
+ String typeName;
+ PsiTypeElement typeElement = psiParameter.getTypeElement();
+ if (typeElement == null) {
+ typeName = "";
+ }
+ else {
+ if (typeElement.getType() instanceof PsiPrimitiveType) {
+ typeName = ((PsiPrimitiveType)typeElement.getType()).getBoxedTypeName();
+ }
+ else {
+ typeName = typeElement.getType().getCanonicalText();
+ }
+ }
+
+ id = typeName + " " + psiParameter.getName();
+ }
+
+ @Override
+ public int compareTo(ParameterWrapper o) {
+ return index - o.index;
+ }
+
+ @Override
+ public String toString() {
+ return id;
+ }
+
+ @NotNull
+ public PsiParameter getPsiParameter() {
+ return psiParameter;
+ }
+ }
+ }
+
+ static class MethodWrapper implements Comparable {
+ private final @NotNull PsiMethod myPsiMethod;
+ private final @NotNull String myId;
+
+ MethodWrapper(@NotNull PsiMethod psiMethod) {
+ this.myPsiMethod = psiMethod;
+
+ List parameterClassNames = new ArrayList();
+ PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+ for (PsiParameter psiParameter : parameters) {
+ parameterClassNames.add(getParameterQualifiedName(psiParameter));
+ }
+
+ myId = initId(psiMethod.getName(), parameterClassNames);
+ }
+
+ private static String getParameterQualifiedName(PsiParameter psiParameter) {
+ PsiTypeElement typeElement = psiParameter.getTypeElement();
+ if (typeElement == null) {
+ return "";
+ }
+
+ if (typeElement.getType() instanceof PsiPrimitiveType) {
+ return ((PsiPrimitiveType)typeElement.getType()).getBoxedTypeName();
+ }
+
+ return typeElement.getType().getCanonicalText();
+ }
+
+ private static String initId(String methodName, List parameterNames) {
+ String shortName = methodName + "(";
+ for (String parameterName : parameterNames) {
+ if (parameterNames.lastIndexOf(".") > -1) {
+ shortName += parameterName.substring(parameterName.lastIndexOf(".") + 1) + ", ";
+ }
+ else {
+ shortName += parameterName + ", ";
+ }
+ }
+
+ if (parameterNames.size() > 0) shortName = shortName.substring(0, shortName.lastIndexOf(", "));
+
+ shortName += ")";
+ return shortName;
+ }
+
+ @NotNull
+ public PsiMethod getPsiMethod() {
+ return myPsiMethod;
+ }
+
+ @NotNull
+ public String getId() {
+ return myId;
+ }
+
+ @Override
+ public String toString() {
+ return myId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MethodWrapper that = (MethodWrapper)o;
+
+ if (!myId.equals(that.myId)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return myId.hashCode();
+ }
+
+ @Override
+ public int compareTo(MethodWrapper o) {
+ return myId.compareTo(o.myId);
+ }
+ }
+}
diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AssertFalse.java b/java/java-tests/testData/inspection/dataFlow/fixture/AssertFalse.java
new file mode 100644
index 000000000000..3aa73afcff5b
--- /dev/null
+++ b/java/java-tests/testData/inspection/dataFlow/fixture/AssertFalse.java
@@ -0,0 +1,9 @@
+public class AssertFalse {
+ void bar() {
+ final boolean b = call();
+ if (Assertions.assertFalse(b)) {
+ if(b) {}
+ }
+ }
+ boolean call() {return true;}
+}
diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNotNull.java b/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNotNull.java
new file mode 100644
index 000000000000..5b16eb78ac7b
--- /dev/null
+++ b/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNotNull.java
@@ -0,0 +1,10 @@
+import java.lang.*;
+
+public class AssertIsNotNull {
+ void bar() {
+ final Object o = call();
+ Assertions.assertIsNotNull(o);
+ if(o == null) {}
+ }
+ Object call() {return new Object();}
+}
diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNull.java b/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNull.java
new file mode 100644
index 000000000000..5915277cc273
--- /dev/null
+++ b/java/java-tests/testData/inspection/dataFlow/fixture/AssertIsNull.java
@@ -0,0 +1,10 @@
+import java.lang.*;
+
+public class AssertIsNull {
+ void bar() {
+ final Object o = call();
+ Assertions.assertIsNull(o);
+ if(o == null) {}
+ }
+ Object call() {return new Object();}
+}
diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AssertTrue.java b/java/java-tests/testData/inspection/dataFlow/fixture/AssertTrue.java
new file mode 100644
index 000000000000..a71e4382ab44
--- /dev/null
+++ b/java/java-tests/testData/inspection/dataFlow/fixture/AssertTrue.java
@@ -0,0 +1,9 @@
+public class AssertTrue {
+ void bar() {
+ final boolean b = call();
+ if (Assertions.assertTrue(b)) {
+ if(b) {}
+ }
+ }
+ boolean call() {return true;}
+}
diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/IsNotNullCheck.java b/java/java-tests/testData/inspection/dataFlow/fixture/IsNotNullCheck.java
new file mode 100644
index 000000000000..85879eda40a7
--- /dev/null
+++ b/java/java-tests/testData/inspection/dataFlow/fixture/IsNotNullCheck.java
@@ -0,0 +1,9 @@
+public class IsNotNullCheck {
+ void bar() {
+ final Value v = call();
+ if (Value.isNotNull(v)) {
+ if(v == null) {}
+ }
+ }
+ Value call() {return new Value();}
+}
diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/IsNullCheck.java b/java/java-tests/testData/inspection/dataFlow/fixture/IsNullCheck.java
new file mode 100644
index 000000000000..1b2c48593a16
--- /dev/null
+++ b/java/java-tests/testData/inspection/dataFlow/fixture/IsNullCheck.java
@@ -0,0 +1,9 @@
+public class IsNullCheck {
+ void bar() {
+ final Value v = call();
+ if (Value.isNull(v)) {
+ if(v == null) {}
+ }
+ }
+ Value call() {return new Value();}
+}
diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java
index eb2ec6a3fd5a..3ba837dc39df 100644
--- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java
+++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java
@@ -16,11 +16,16 @@
package com.intellij.codeInspection;
import com.intellij.JavaTestUtil;
-import com.intellij.codeInsight.NullableNotNullManager;
+import com.intellij.codeInsight.*;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.Disposer;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiMethod;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jetbrains.annotations.*;
+
+import java.io.IOException;
/**
* @author peter
@@ -159,4 +164,62 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
public void testEqualsHasNoSideEffects() { doTest(); }
+ public void testIsNullCheck() throws Exception {
+ ConditionCheckManager.getInstance(myModule.getProject()).getIsNullCheckMethods().add(
+ buildConditionChecker("Value", "isNull", ConditionChecker.Type.IS_NULL_METHOD,
+ "public class Value { public static boolean isNull(Value o) {if (o == null) return true; else return false;} }"));
+ doTest();
+ }
+
+ public void testIsNotNullCheck() throws Exception {
+ ConditionCheckManager.getInstance(myModule.getProject()).getIsNotNullCheckMethods().add(
+ buildConditionChecker("Value", "isNotNull", ConditionChecker.Type.IS_NOT_NULL_METHOD,
+ "public class Value { public static boolean isNotNull(Value o) {if (o == null) return false; else return true;} }"));
+ doTest();
+ }
+
+ public void testAssertTrue() throws Exception {
+ ConditionCheckManager.getInstance(myModule.getProject()).getAssertTrueMethods().add(
+ buildConditionChecker("Assertions", "assertTrue", ConditionChecker.Type.ASSERT_TRUE_METHOD,
+ "public class Assertions { public static boolean assertTrue(boolean b) {if(!b) throw new Exception();} }"));
+ doTest();
+ }
+
+ public void testAssertFalse() throws Exception {
+ ConditionCheckManager.getInstance(myModule.getProject()).getAssertFalseMethods().add(
+ buildConditionChecker("Assertions", "assertFalse", ConditionChecker.Type.ASSERT_FALSE_METHOD,
+ "public class Assertions { public static boolean assertFalse(boolean b) {if(b) throw new Exception();} }"));
+ doTest();
+ }
+
+ public void testAssertIsNull() throws Exception {
+ ConditionCheckManager.getInstance(myModule.getProject()).getAssertIsNullMethods().add(
+ buildConditionChecker("Assertions", "assertIsNull", ConditionChecker.Type.ASSERT_IS_NULL_METHOD,
+ "public class Assertions { public static boolean assertIsNull(Object o) {if(o != null) throw new Exception();} }"));
+ doTest();
+ }
+
+ public void testAssertIsNotNull() throws Exception {
+ ConditionCheckManager.getInstance(myModule.getProject()).getAssertIsNotNullMethods().add(
+ buildConditionChecker("Assertions", "assertIsNotNull", ConditionChecker.Type.ASSERT_IS_NOT_NULL_METHOD,
+ "public class Assertions { public static boolean assertIsNotNull(Object o) {if(o == null) throw new Exception();} }"));
+ doTest();
+ }
+
+ @Nullable
+ private ConditionChecker buildConditionChecker(String className, String methodName, ConditionChecker.Type type, String classText)
+ throws IOException {
+ myFixture.addClass(classText);
+ PsiClass psiClass = myFixture.findClass(className);
+ PsiMethod psiMethod = null;
+ PsiMethod[] methods = psiClass.getMethods();
+ for (PsiMethod tempPsiMethod : methods) {
+ if (tempPsiMethod.getName().equals(methodName)) {
+ psiMethod = tempPsiMethod;
+ break;
+ }
+ }
+ assert psiMethod != null;
+ return new ConditionChecker.FromPsiBuilder(psiMethod, psiMethod.getParameterList().getParameters()[0], type).build();
+ }
}
diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties
index 084f692e3dc8..663c46229631 100644
--- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties
+++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties
@@ -51,6 +51,22 @@ inspection.data.flow.redundant.instanceof.quickfix=Replace with != null
inspection.data.flow.simplify.boolean.expression.quickfix=Simplify Boolean Expression
inspection.data.flow.simplify.to.assignment.quickfix.name=Simplify to normal assignment
configure.annotations.option=Configure annotations
+configure.checker.option.button=Configure IsNull/IsNotNull/True/False Check/Assert Methods
+configure.checker.option.main.dialog.title=IsNull/IsNotNull Configuration
+configure.checker.option.overlap.error.title=Overlapping Check
+configure.checker.option.overlap.error.msg=Configuration conflicts with
+configure.checker.option.isNull.add.method.checker.dialog.title=Add IsNull Check Method
+configure.checker.option.isNotNull.add.method.checker.dialog.title=Add IsNotNull Check Method
+configure.checker.option.assert.isNull.add.method.checker.dialog.title=Add Assert IsNull Method
+configure.checker.option.assert.isNotNull.add.method.checker.dialog.title=Add Assert IsNotNull Method
+configure.checker.option.assert.true.add.method.checker.dialog.title=Add Assert True Method
+configure.checker.option.assert.false.add.method.checker.dialog.title=Add Assert False Method
+configure.checker.option.assert.isNull.method.panel.title=Assert IsNull Methods
+configure.checker.option.assert.isNotNull.method.panel.title=Assert IsNotNull Methods
+configure.checker.option.isNull.method.panel.title=IsNull Check Methods
+configure.checker.option.isNotNull.method.panel.title=IsNotNull Check Methods
+configure.checker.option.assert.true.method.panel.title=Assert True Methods
+configure.checker.option.assert.false.method.panel.title=Assert False Methods
#messages from dataflow inspection
dataflow.message.npe.method.invocation=Method invocation #ref #loc may produce java.lang.NullPointerException
diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml
index a9768a9a1cef..3c635fe04277 100644
--- a/resources/src/META-INF/IdeaPlugin.xml
+++ b/resources/src/META-INF/IdeaPlugin.xml
@@ -433,6 +433,9 @@
+
+