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/codeInsight/TailTypes.java b/java/java-impl/src/com/intellij/codeInsight/TailTypes.java
index 43def9006a28..923dd8213019 100644
--- a/java/java-impl/src/com/intellij/codeInsight/TailTypes.java
+++ b/java/java-impl/src/com/intellij/codeInsight/TailTypes.java
@@ -118,24 +118,10 @@ public class TailTypes {
return styleSettings.SPACE_WITHIN_IF_PARENTHESES;
}
};
- public static final TailType FINALLY_LBRACE = new BracesTailType() {
- @Override
- protected boolean isSpaceBeforeLBrace(final CommonCodeStyleSettings styleSettings, final Editor editor, final int tailOffset) {
- return styleSettings.SPACE_BEFORE_FINALLY_LBRACE;
- }
- };
- public static final TailType TRY_LBRACE = new BracesTailType() {
- @Override
- protected boolean isSpaceBeforeLBrace(CommonCodeStyleSettings styleSettings, Editor editor, int tailOffset) {
- return styleSettings.SPACE_BEFORE_TRY_LBRACE;
- }
- };
- public static final TailType DO_LBRACE = new BracesTailType() {
- @Override
- protected boolean isSpaceBeforeLBrace(CommonCodeStyleSettings styleSettings, Editor editor, int tailOffset) {
- return styleSettings.SPACE_BEFORE_DO_LBRACE;
- }
- };
+ private static final TailType BRACES = new BracesTailType();
+ public static final TailType FINALLY_LBRACE = BRACES;
+ public static final TailType TRY_LBRACE = BRACES;
+ public static final TailType DO_LBRACE = BRACES;
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java
index 6d0b45fff5c4..eb19c574c872 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaChainLookupElement.java
@@ -15,10 +15,7 @@
*/
package com.intellij.codeInsight.completion;
-import com.intellij.codeInsight.lookup.LookupElement;
-import com.intellij.codeInsight.lookup.LookupElementDecorator;
-import com.intellij.codeInsight.lookup.LookupElementPresentation;
-import com.intellij.codeInsight.lookup.TypedLookupItem;
+import com.intellij.codeInsight.lookup.*;
import com.intellij.diagnostic.LogMessageEx;
import com.intellij.diagnostic.errordialog.Attachment;
import com.intellij.openapi.diagnostic.Logger;
@@ -97,8 +94,9 @@ public class JavaChainLookupElement extends LookupElementDecorator propertyHandlers = new ArrayList();
- @Override
- public void run() {
- final List propertyHandlers = new ArrayList();
- final PsiField[] fields = psiClass.getFields();
+ for (final PsiField field : psiClass.getFields()) {
+ if (field == element) continue;
- for (final PsiField field : fields) {
- if (field == element) continue;
+ assert field.isValid() : "invalid field: " + field;
+ PsiType fieldType = field.getType();
+ assert fieldType.isValid() : "invalid field type: " + field + "; " + fieldType + " of " + fieldType.getClass();
- assert field.isValid() : "invalid field: " + field;
- PsiType fieldType = field.getType();
- assert fieldType.isValid() : "invalid field type: " + field + "; " + fieldType + " of " + fieldType.getClass();
+ final PsiModifierList modifierList = field.getModifierList();
+ if (staticContext && (modifierList != null && !modifierList.hasModifierProperty(PsiModifier.STATIC))) continue;
- final PsiModifierList modifierList = field.getModifierList();
- if (staticContext && (modifierList != null && !modifierList.hasModifierProperty(PsiModifier.STATIC))) continue;
+ if (fieldType.equals(varType)) {
+ final String getterName = PropertyUtil.suggestGetterName(field.getProject(), field);
+ if ((psiClass.findMethodsByName(getterName, true).length == 0 ||
+ psiClass.findMethodBySignature(PropertyUtil.generateGetterPrototype(field), true) == null)) {
+ propertyHandlers.add(getterName);
+ }
+ }
- if (fieldType.equals(varType)) {
- final String getterName = PropertyUtil.suggestGetterName(field.getProject(), field);
- if ((psiClass.findMethodsByName(getterName, true).length == 0 ||
- psiClass.findMethodBySignature(PropertyUtil.generateGetterPrototype(field), true) == null)) {
- propertyHandlers.add(getterName);
- }
- }
-
- if (PsiType.VOID.equals(varType)) {
- final String setterName = PropertyUtil.suggestSetterName(field.getProject(), field);
- if ((psiClass.findMethodsByName(setterName, true).length == 0 ||
- psiClass.findMethodBySignature(PropertyUtil.generateSetterPrototype(field), true) == null)) {
- propertyHandlers.add(setterName);
- }
- }
+ if (PsiType.VOID.equals(varType)) {
+ final String setterName = PropertyUtil.suggestSetterName(field.getProject(), field);
+ if ((psiClass.findMethodsByName(setterName, true).length == 0 ||
+ psiClass.findMethodBySignature(PropertyUtil.generateSetterPrototype(field), true) == null)) {
+ propertyHandlers.add(setterName);
}
- result = ArrayUtil.toStringArray(propertyHandlers);
}
}
- final Change result = new Change();
- CodeStyleManager.getInstance(element.getProject()).performActionWithFormatterDisabled(result);
- return result.result;
+
+ return ArrayUtil.toStringArray(propertyHandlers);
}
private static void addLookupItems(Set lookupElements, @Nullable final SuggestedNameInfo callback, PrefixMatcher matcher, Project project, String... strings) {
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaPsiClassReferenceElement.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaPsiClassReferenceElement.java
index 349cb7b47890..c1bfeda87531 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaPsiClassReferenceElement.java
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaPsiClassReferenceElement.java
@@ -152,7 +152,7 @@ public class JavaPsiClassReferenceElement extends LookupItem