java property index

This commit is contained in:
Dmitry Batkovich
2017-09-01 19:03:31 +03:00
parent 3a2a54275f
commit 86829cac3e
21 changed files with 908 additions and 478 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -17,15 +17,12 @@ package com.intellij.codeInspection.dataFlow
import com.intellij.lang.LighterAST
import com.intellij.lang.LighterASTNode
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.psi.impl.source.JavaLightStubBuilder
import com.intellij.psi.impl.source.PsiFileImpl
import com.intellij.psi.impl.source.PsiMethodImpl
import com.intellij.psi.impl.source.tree.JavaElementType
import com.intellij.psi.impl.source.tree.JavaElementType.*
import com.intellij.psi.impl.source.tree.LightTreeUtil
import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor
import com.intellij.psi.util.PsiUtil
import com.intellij.psi.stub.JavaStubImplUtil
import com.intellij.util.gist.GistManager
import java.util.*
@@ -96,18 +93,4 @@ private fun createData(body: LighterASTNode,
return MethodData(nullity, purity, contracts, notNullParams, body.startOffset, body.endOffset)
}
fun getIndexedData(method: PsiMethodImpl): MethodData? = gist.getFileData(method.containingFile)?.get(methodIndex(method))
private fun methodIndex(method: PsiMethodImpl): Int? {
val file = method.containingFile as PsiFileImpl
if (file.elementTypeForStubBuilder == null) return null
val stubTree = try {
file.stubTree ?: file.calcStubTree()
} catch (e: ProcessCanceledException) {
throw e
} catch (e: RuntimeException) {
throw RuntimeException("While inferring contract for " + PsiUtil.getMemberQualifiedName(method), e)
}
return stubTree.plainList.filter { it.stubType == JavaElementType.METHOD }.map { it.psi }.indexOf(method)
}
fun getIndexedData(method: PsiMethodImpl): MethodData? = gist.getFileData(method.containingFile)?.get(JavaStubImplUtil.getMethodStubIndex(method))

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -73,13 +73,12 @@ public class PreferByKindWeigher extends LookupElementWeigher {
psiElement(PsiReferenceExpression.class).withParent(PsiResourceExpression.class)));
private final CompletionType myCompletionType;
@NotNull
private final PsiElement myPosition;
private final Set<PsiField> myNonInitializedFields;
private final Condition<PsiClass> myRequiredSuper;
private final ExpectedTypeInfo[] myExpectedTypes;
public PreferByKindWeigher(CompletionType completionType, @NotNull final PsiElement position, ExpectedTypeInfo[] expectedTypes) {
public PreferByKindWeigher(CompletionType completionType, final PsiElement position, ExpectedTypeInfo[] expectedTypes) {
super("kind");
myCompletionType = completionType;
myPosition = position;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2017 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.

View File

@@ -15,5 +15,6 @@
<orderEntry type="module" module-name="projectModel-impl" />
<orderEntry type="module" module-name="java-indexing-api" exported="" />
<orderEntry type="library" name="Guava" level="project" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
</module>

View File

@@ -0,0 +1,201 @@
/*
* Copyright 2000-2017 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.psi.impl
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.lang.LighterASTNode
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.JavaTokenType
import com.intellij.psi.PsiField
import com.intellij.psi.impl.cache.RecordUtil
import com.intellij.psi.impl.java.stubs.JavaStubElementTypes
import com.intellij.psi.impl.source.JavaLightStubBuilder
import com.intellij.psi.impl.source.JavaLightTreeUtil
import com.intellij.psi.impl.source.PsiMethodImpl
import com.intellij.psi.impl.source.tree.ElementType
import com.intellij.psi.impl.source.tree.JavaElementType
import com.intellij.psi.impl.source.tree.LightTreeUtil
import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.stub.JavaStubImplUtil
import com.intellij.psi.util.PropertyUtil
import com.intellij.psi.util.PropertyUtilBase
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.indexing.*
import com.intellij.util.io.DataExternalizer
import com.intellij.util.io.EnumeratorIntegerDescriptor
import com.intellij.util.io.EnumeratorStringDescriptor
import com.intellij.util.io.KeyDescriptor
import java.io.DataInput
import java.io.DataOutput
private val indexId = ID.create<Int, PropertyIndexValue>("java.simple.property")
private val log = Logger.getInstance(JavaSimplePropertyIndex::class.java)
fun getFieldOfGetter(method: PsiMethodImpl): PsiField? = resolveFieldFromIndexValue(method, true)
fun getFieldOfSetter(method: PsiMethodImpl): PsiField? = resolveFieldFromIndexValue(method, false)
private fun resolveFieldFromIndexValue(method: PsiMethodImpl, isGetter: Boolean): PsiField? {
val id = JavaStubImplUtil.getMethodStubIndex(method)
if (id != -1) {
val values = FileBasedIndex.getInstance().getValues(indexId, id, GlobalSearchScope.fileScope(method.containingFile))
when (values.size) {
0 -> return null
1 -> {
val indexValue = values[0]
if (isGetter != indexValue.getter) return null
val psiClass = method.containingClass
val project = psiClass!!.project
val expr = JavaPsiFacade.getElementFactory(project).createExpressionFromText(indexValue.propertyRefText, psiClass)
return PropertyUtil.getSimplyReturnedField(expr)
}
else -> {
log.error("multiple index values for method $method")
}
}
}
return null
}
data class PropertyIndexValue(val propertyRefText: String, val getter: Boolean)
class JavaSimplePropertyIndex : FileBasedIndexExtension<Int, PropertyIndexValue>(), PsiDependentIndex {
override fun getIndexer(): DataIndexer<Int, PropertyIndexValue, FileContent> = DataIndexer { inputData ->
val result = ContainerUtil.newHashMap<Int, PropertyIndexValue>()
val tree = (inputData as FileContentImpl).lighterASTForPsiDependentIndex
object : RecursiveLighterASTNodeWalkingVisitor(tree) {
var methodIndex = 0
override fun visitNode(element: LighterASTNode) {
if (JavaLightStubBuilder.isCodeBlockWithoutStubs(element)) return
if (element.tokenType === JavaElementType.METHOD) {
extractProperty(element)?.let { result.put(methodIndex, it) }
methodIndex++
}
super.visitNode(element)
}
private fun extractProperty(method: LighterASTNode): PropertyIndexValue? {
var isConstructor = true
var isGetter = true
var isBooleanReturnType = false
var isVoidReturnType = false
var setterParameterName: String? = null
var refText: String? = null
for (child in tree.getChildren(method)) {
when (child.tokenType) {
JavaElementType.TYPE -> {
val children = tree.getChildren(child)
if (children.size != 1) return null
val typeElement = children[0]
if (typeElement.tokenType == JavaTokenType.VOID_KEYWORD) isVoidReturnType = true
if (typeElement.tokenType == JavaTokenType.BOOLEAN_KEYWORD) isBooleanReturnType = true
isConstructor = false
}
JavaElementType.PARAMETER_LIST -> {
if (isGetter) {
if (LightTreeUtil.firstChildOfType(tree, child, JavaElementType.PARAMETER) != null) return null
} else {
val parameters = LightTreeUtil.getChildrenOfType(tree, child, JavaElementType.PARAMETER)
if (parameters.size != 1) return null
setterParameterName = JavaLightTreeUtil.getNameIdentifierText(tree, parameters[0])
if (setterParameterName == null) return null
}
}
JavaElementType.CODE_BLOCK -> {
refText = if (isGetter) getGetterPropertyRefText(child) else getSetterPropertyRefText(child, setterParameterName!!)
if (refText == null) return null
}
JavaTokenType.IDENTIFIER -> {
if (isConstructor) return null
val name = RecordUtil.intern(tree.charTable, child)
val flavour = PropertyUtil.getMethodNameGetterFlavour(name)
when (flavour) {
PropertyUtilBase.GetterFlavour.NOT_A_GETTER -> {
if (PropertyUtil.isSetterName(name)) {
isGetter = false
}
else {
return null
}
}
PropertyUtilBase.GetterFlavour.BOOLEAN -> if (!isBooleanReturnType) return null
else -> { }
}
if (isVoidReturnType && isGetter) return null
}
}
}
return refText?.let { PropertyIndexValue(it, isGetter) }
}
private fun getSetterPropertyRefText(codeBlock: LighterASTNode, setterParameterName: String): String? {
val assignment = tree
.getChildren(codeBlock)
.singleOrNull { ElementType.JAVA_STATEMENT_BIT_SET.contains(it.tokenType) }
?.takeIf { it.tokenType == JavaElementType.EXPRESSION_STATEMENT }
?.let { LightTreeUtil.firstChildOfType(tree, it, JavaElementType.ASSIGNMENT_EXPRESSION) }
if (assignment == null || LightTreeUtil.firstChildOfType(tree, assignment, JavaTokenType.EQ) == null) return null
val operands = LightTreeUtil.getChildrenOfType(tree, assignment, ElementType.EXPRESSION_BIT_SET)
if (operands.size != 2 || LightTreeUtil.toFilteredString(tree, operands[1], null) != setterParameterName) return null
val lhsText = LightTreeUtil.toFilteredString(tree, operands[0], null)
if (lhsText == setterParameterName) return null
return lhsText
}
private fun getGetterPropertyRefText(codeBlock: LighterASTNode): String? = tree
.getChildren(codeBlock)
.singleOrNull { ElementType.JAVA_STATEMENT_BIT_SET.contains(it.tokenType) }
?.takeIf { it.tokenType == JavaElementType.RETURN_STATEMENT}
?.let { LightTreeUtil.firstChildOfType(tree, it, ElementType.EXPRESSION_BIT_SET) }
?.let { LightTreeUtil.toFilteredString(tree, it, null) }
}.visitNode(tree.root)
result
}
override fun getKeyDescriptor(): KeyDescriptor<Int> = EnumeratorIntegerDescriptor.INSTANCE
override fun getValueExternalizer(): DataExternalizer<PropertyIndexValue> = object: DataExternalizer<PropertyIndexValue> {
override fun save(out: DataOutput, value: PropertyIndexValue?) {
value!!
EnumeratorStringDescriptor.INSTANCE.save(out, value.propertyRefText)
out.writeBoolean(value.getter)
}
override fun read(input: DataInput): PropertyIndexValue = PropertyIndexValue(EnumeratorStringDescriptor.INSTANCE.read(input), input.readBoolean())
}
override fun getName(): ID<Int, PropertyIndexValue> = indexId
override fun getInputFilter(): FileBasedIndex.InputFilter = object : DefaultFileTypeSpecificInputFilter(JavaFileType.INSTANCE) {
override fun acceptInput(file: VirtualFile): Boolean = JavaStubElementTypes.JAVA_FILE.shouldBuildStubFor(file)
}
override fun dependsOnFileContent(): Boolean = true
override fun getVersion(): Int = 0
}

View File

@@ -0,0 +1,257 @@
/*
* Copyright 2000-2017 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.psi.util;
import com.intellij.lang.jvm.JvmModifier;
import com.intellij.psi.*;
import com.intellij.psi.impl.JavaSimplePropertyIndexKt;
import com.intellij.psi.impl.source.PsiMethodImpl;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
/**
* @author Mike
*/
public class PropertyUtil extends PropertyUtilBase {
private PropertyUtil() {
}
@Nullable
public static PsiField getFieldOfGetter(PsiMethod method) {
return getFieldOfGetter(method, true);
}
@Nullable
private static PsiField getFieldOfGetter(PsiMethod method, boolean useIndex) {
PsiField field = useIndex && method instanceof PsiMethodImpl && method.isPhysical()
? JavaSimplePropertyIndexKt.getFieldOfGetter((PsiMethodImpl)method)
: getSimplyReturnedField(getGetterReturnExpression(method));
if (field == null || !checkFieldLocation(method, field)) return null;
final PsiType returnType = method.getReturnType();
return returnType != null && field.getType().equals(returnType) ? field : null;
}
@Nullable
public static PsiField getSimplyReturnedField(@Nullable PsiExpression value) {
if (!(value instanceof PsiReferenceExpression)) {
return null;
}
final PsiReferenceExpression reference = (PsiReferenceExpression)value;
if (hasSubstantialQualifier(reference)) {
return null;
}
final PsiElement referent = reference.resolve();
if (!(referent instanceof PsiField)) {
return null;
}
return (PsiField)referent;
}
private static boolean hasSubstantialQualifier(PsiReferenceExpression reference) {
final PsiExpression qualifier = reference.getQualifierExpression();
if (qualifier == null) return false;
if (qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) {
return false;
}
if (qualifier instanceof PsiReferenceExpression) {
return !(((PsiReferenceExpression)qualifier).resolve() instanceof PsiClass);
}
return true;
}
public static boolean isSimpleGetter(PsiMethod method) {
//noinspection TestOnlyProblems
return isSimpleGetter(method, true);
}
@TestOnly
public static boolean isSimpleGetter(PsiMethod method, boolean useIndex) {
return getFieldOfGetter(method, useIndex) != null;
}
@Nullable
public static PsiField getFieldOfSetter(@Nullable PsiMethod method) {
return getFieldOfSetter(method, true);
}
@Nullable
private static PsiField getFieldOfSetter(@Nullable PsiMethod method, boolean useIndex) {
if (method == null) {
return null;
}
final PsiParameterList parameterList = method.getParameterList();
if (parameterList.getParametersCount() != 1) {
return null;
}
PsiField field;
if (useIndex && method instanceof PsiMethodImpl && method.isPhysical()) {
field = JavaSimplePropertyIndexKt.getFieldOfSetter((PsiMethodImpl)method);
}
else {
@NonNls final String name = method.getName();
if (!name.startsWith("set")) {
return null;
}
if (method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {
return null;
}
final PsiCodeBlock body = method.getBody();
if (body == null) {
return null;
}
final PsiStatement[] statements = body.getStatements();
if (statements.length != 1) {
return null;
}
final PsiStatement statement = statements[0];
if (!(statement instanceof PsiExpressionStatement)) {
return null;
}
final PsiExpressionStatement possibleAssignmentStatement = (PsiExpressionStatement)statement;
final PsiExpression possibleAssignment = possibleAssignmentStatement.getExpression();
if (!(possibleAssignment instanceof PsiAssignmentExpression)) {
return null;
}
final PsiAssignmentExpression assignment = (PsiAssignmentExpression)possibleAssignment;
if (!JavaTokenType.EQ.equals(assignment.getOperationTokenType())) {
return null;
}
final PsiExpression lhs = assignment.getLExpression();
if (!(lhs instanceof PsiReferenceExpression)) {
return null;
}
final PsiReferenceExpression reference = (PsiReferenceExpression)lhs;
final PsiExpression qualifier = reference.getQualifierExpression();
if (qualifier instanceof PsiReferenceExpression) {
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier;
final PsiElement target = referenceExpression.resolve();
if (!(target instanceof PsiClass)) {
return null;
}
}
else if (qualifier != null && !(qualifier instanceof PsiThisExpression) && !(qualifier instanceof PsiSuperExpression)) {
return null;
}
final PsiElement referent = reference.resolve();
if (referent == null) {
return null;
}
if (!(referent instanceof PsiField)) {
return null;
}
field = (PsiField)referent;
final PsiExpression rhs = assignment.getRExpression();
if (!(rhs instanceof PsiReferenceExpression)) {
return null;
}
final PsiReferenceExpression rReference = (PsiReferenceExpression)rhs;
final PsiExpression rQualifier = rReference.getQualifierExpression();
if (rQualifier != null) {
return null;
}
final PsiElement rReferent = rReference.resolve();
if (rReferent == null) {
return null;
}
if (!(rReferent instanceof PsiParameter)) {
return null;
}
}
return field != null && field.getType().equals(parameterList.getParameters()[0].getType()) && checkFieldLocation(method, field)
? field
: null;
}
public static boolean isSimpleSetter(PsiMethod method) {
//noinspection TestOnlyProblems
return isSimpleSetter(method, true);
}
@TestOnly
public static boolean isSimpleSetter(PsiMethod method, boolean useIndex) {
return getFieldOfSetter(method, useIndex) != null;
}
@Nullable
public static PsiMethod getReversePropertyMethod(PsiMethod propertyMethod) {
if (propertyMethod == null) {
return null;
}
final PsiClass aClass = propertyMethod.getContainingClass();
if (aClass == null) {
return null;
}
final String methodName = propertyMethod.getName();
final String prefix;
if (methodName.startsWith("get")) {
prefix = "get";
}
else if (methodName.startsWith(IS_PREFIX)) {
prefix = IS_PREFIX;
}
else if (methodName.startsWith("set")) {
prefix = "set";
}
else {
return null;
}
final String name = methodName.substring(prefix.length());
final PsiField field = prefix.equals("set") ? getFieldOfSetter(propertyMethod) : getFieldOfGetter(propertyMethod);
if (field == null) {
return null;
}
if (prefix.equals("set")) {
final PsiMethod result = findPropertyMethod(aClass, "get", name, field);
if (result != null) {
return result;
}
return findPropertyMethod(aClass, IS_PREFIX, name, field);
}
else {
return findPropertyMethod(aClass, "set", name, field);
}
}
private static PsiMethod findPropertyMethod(@NotNull PsiClass aClass,
@NotNull String prefix,
@NotNull String propertyName,
@NotNull PsiField field1) {
final PsiMethod[] methods = aClass.findMethodsByName(prefix + propertyName, true);
for (PsiMethod method : methods) {
final PsiField field2 = prefix.equals("set") ? getFieldOfSetter(method) : getFieldOfGetter(method);
if (field1.equals(field2)) {
return method;
}
}
return null;
}
private static boolean checkFieldLocation(PsiMethod method, PsiField field) {
return PsiResolveHelper.SERVICE.getInstance(method.getProject()).isAccessible(field, method, null) &&
(!method.hasModifier(JvmModifier.STATIC) || field.hasModifier(JvmModifier.STATIC)) &&
InheritanceUtil.isInheritorOrSelf(method.getContainingClass(), field.getContainingClass(), true);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -35,101 +35,14 @@ import org.jetbrains.annotations.Nullable;
import java.beans.Introspector;
import java.util.*;
/**
* @author Mike
*/
public class PropertyUtil {
@NonNls private static final String IS_PREFIX = "is";
public class PropertyUtilBase {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.PropertyUtil");
private PropertyUtil() {
}
@Contract("null -> false")
public static boolean isSimplePropertyGetter(@Nullable PsiMethod method) {
return hasGetterName(method) && method.getParameterList().getParametersCount() == 0;
}
@SuppressWarnings("HardCodedStringLiteral")
public static boolean hasGetterName(final PsiMethod method) {
if (method == null) return false;
if (method.isConstructor()) return false;
String methodName = method.getName();
int methodNameLength = methodName.length();
if (methodName.startsWith("get") && methodNameLength > "get".length()) {
if (Character.isLowerCase(methodName.charAt("get".length()))
&& (methodNameLength == "get".length() + 1 || Character.isLowerCase(methodName.charAt("get".length() + 1)))) {
return false;
}
PsiType returnType = method.getReturnType();
if (returnType != null && PsiType.VOID.equals(returnType)) return false;
}
else if (methodName.startsWith(IS_PREFIX) && methodNameLength > IS_PREFIX.length()) {
if (Character.isLowerCase(methodName.charAt(IS_PREFIX.length()))
&& (methodNameLength == IS_PREFIX.length() + 1 || Character.isLowerCase(methodName.charAt(IS_PREFIX.length() + 1)))) {
return false;
}
PsiType returnType = method.getReturnType();
return isBoolean(returnType);
}
else {
return false;
}
return true;
}
@SuppressWarnings("HardCodedStringLiteral")
public static boolean isSimplePropertySetter(@Nullable PsiMethod method) {
if (method == null) return false;
if (method.isConstructor()) return false;
String methodName = method.getName();
if (!(methodName.startsWith("set") && methodName.length() > "set".length())) return false;
if (Character.isLowerCase(methodName.charAt("set".length()))
&& (methodName.length() == "set".length() + 1 || Character.isLowerCase(methodName.charAt("set".length() + 1)))) {
return false;
}
if (method.getParameterList().getParametersCount() != 1) {
return false;
}
final PsiType returnType = method.getReturnType();
if (returnType == null || PsiType.VOID.equals(returnType)) {
return true;
}
return Comparing.equal(PsiUtil.resolveClassInType(TypeConversionUtil.erasure(returnType)), method.getContainingClass());
}
@NonNls protected static final String IS_PREFIX = "is";
@Nullable
public static String getPropertyName(@NotNull PsiMethod method) {
if (isSimplePropertyGetter(method)) {
return getPropertyNameByGetter(method);
}
if (isSimplePropertySetter(method)) {
return getPropertyNameBySetter(method);
}
return null;
}
@NotNull
public static String getPropertyNameByGetter(PsiMethod getterMethod) {
@NonNls String methodName = getterMethod.getName();
if (methodName.startsWith("get")) return StringUtil.decapitalize(methodName.substring(3));
if (methodName.startsWith("is")) return StringUtil.decapitalize(methodName.substring(2));
return methodName;
}
@NotNull
public static String getPropertyNameBySetter(@NotNull PsiMethod setterMethod) {
String methodName = setterMethod.getName();
return Introspector.decapitalize(methodName.substring(3));
public static String getPropertyName(@NonNls @NotNull String methodName) {
return StringUtil.getPropertyName(methodName);
}
@NotNull
@@ -207,6 +120,92 @@ public class PropertyUtil {
return ContainerUtil.concat(getGetters(psiClass, propertyName), getSetters(psiClass, propertyName));
}
@NotNull
public static String[] getReadableProperties(@NotNull PsiClass aClass, boolean includeSuperClass) {
List<String> result = new ArrayList<>();
PsiMethod[] methods = includeSuperClass ? aClass.getAllMethods() : aClass.getMethods();
for (PsiMethod method : methods) {
if (CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) continue;
if (isSimplePropertyGetter(method)) {
result.add(getPropertyName(method));
}
}
return ArrayUtil.toStringArray(result);
}
@NotNull
public static String[] getWritableProperties(@NotNull PsiClass aClass, boolean includeSuperClass) {
List<String> result = new ArrayList<>();
PsiMethod[] methods = includeSuperClass ? aClass.getAllMethods() : aClass.getMethods();
for (PsiMethod method : methods) {
if (CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) continue;
if (isSimplePropertySetter(method)) {
result.add(getPropertyName(method));
}
}
return ArrayUtil.toStringArray(result);
}
@Nullable
public static PsiType getPropertyType(final PsiMember member) {
if (member instanceof PsiField) {
return ((PsiField)member).getType();
}
if (member instanceof PsiMethod) {
final PsiMethod psiMethod = (PsiMethod)member;
if (isSimplePropertyGetter(psiMethod)) {
return psiMethod.getReturnType();
}
else if (isSimplePropertySetter(psiMethod)) {
return psiMethod.getParameterList().getParameters()[0].getType();
}
}
return null;
}
@Nullable
public static PsiMethod findPropertySetter(PsiClass aClass,
@NotNull String propertyName,
boolean isStatic,
boolean checkSuperClasses) {
if (aClass == null) return null;
String setterName = suggestSetterName(propertyName);
PsiMethod[] methods = aClass.findMethodsByName(setterName, checkSuperClasses);
for (PsiMethod method : methods) {
if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
if (isSimplePropertySetter(method)) {
if (getPropertyNameBySetter(method).equals(propertyName)) {
return method;
}
}
}
return null;
}
@Nullable
public static PsiField findPropertyField(PsiClass aClass, String propertyName, boolean isStatic) {
PsiField[] fields = aClass.getAllFields();
for (PsiField field : fields) {
if (field.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
if (propertyName.equals(suggestPropertyName(field))) return field;
}
return null;
}
@Nullable
public static PsiMethod findPropertyGetter(PsiClass aClass,
@NotNull String propertyName,
@@ -249,28 +248,6 @@ public class PropertyUtil {
return isSimplePropertyGetter(method) || isSimplePropertySetter(method);
}
@Nullable
public static PsiMethod findPropertySetter(PsiClass aClass,
@NotNull String propertyName,
boolean isStatic,
boolean checkSuperClasses) {
if (aClass == null) return null;
String setterName = suggestSetterName(propertyName);
PsiMethod[] methods = aClass.findMethodsByName(setterName, checkSuperClasses);
for (PsiMethod method : methods) {
if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
if (isSimplePropertySetter(method)) {
if (getPropertyNameBySetter(method).equals(propertyName)) {
return method;
}
}
}
return null;
}
@Nullable
public static PsiMethod findPropertySetterWithType(String propertyName, boolean isStatic, PsiType type, Iterator<PsiMethod> methods) {
while (methods.hasNext()) {
@@ -287,21 +264,158 @@ public class PropertyUtil {
return null;
}
@Nullable
public static PsiField findPropertyField(PsiClass aClass, String propertyName, boolean isStatic) {
PsiField[] fields = aClass.getAllFields();
for (PsiField field : fields) {
if (field.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
if (propertyName.equals(suggestPropertyName(field))) return field;
public enum GetterFlavour {
BOOLEAN,
GENERIC,
NOT_A_GETTER
}
@NotNull
public static GetterFlavour getMethodNameGetterFlavour(@NotNull String methodName) {
if (checkPrefix(methodName, "get")) {
return GetterFlavour.GENERIC;
}
else if (checkPrefix(methodName, IS_PREFIX)) {
return GetterFlavour.BOOLEAN;
}
return GetterFlavour.NOT_A_GETTER;
}
@Contract("null -> false")
public static boolean isSimplePropertyGetter(@Nullable PsiMethod method) {
return hasGetterName(method) && method.getParameterList().getParametersCount() == 0;
}
@SuppressWarnings("HardCodedStringLiteral")
public static boolean hasGetterName(final PsiMethod method) {
if (method == null) return false;
if (method.isConstructor()) return false;
String methodName = method.getName();
GetterFlavour flavour = getMethodNameGetterFlavour(methodName);
switch (flavour) {
case GENERIC:
PsiType returnType = method.getReturnType();
return returnType == null || !PsiType.VOID.equals(returnType);
case BOOLEAN:
return isBoolean(method.getReturnType());
case NOT_A_GETTER:
default:
return false;
}
}
private static boolean isBoolean(@Nullable PsiType propertyType) {
return PsiType.BOOLEAN.equals(propertyType);
}
public static String suggestPropertyName(@NotNull PsiField field) {
return suggestPropertyName(field, field.getName());
}
@NotNull
public static String suggestPropertyName(@NotNull PsiField field, @NotNull String fieldName) {
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(field.getProject());
VariableKind kind = codeStyleManager.getVariableKind(field);
String name = codeStyleManager.variableNameToPropertyName(fieldName, kind);
if (!field.hasModifierProperty(PsiModifier.STATIC) && isBoolean(field.getType())) {
if (name.startsWith(IS_PREFIX) && name.length() > IS_PREFIX.length() && Character.isUpperCase(name.charAt(IS_PREFIX.length()))) {
name = Introspector.decapitalize(name.substring(IS_PREFIX.length()));
}
}
return name;
}
public static String suggestGetterName(PsiField field) {
String propertyName = suggestPropertyName(field);
return suggestGetterName(propertyName, field.getType());
}
public static String suggestSetterName(PsiField field) {
String propertyName = suggestPropertyName(field);
return suggestSetterName(propertyName);
}
@Nullable
public static String getPropertyName(final PsiMember member) {
if (member instanceof PsiMethod) {
return getPropertyName((PsiMethod)member);
}
if (member instanceof PsiField) {
return member.getName();
}
return null;
}
@SuppressWarnings("HardCodedStringLiteral")
public static boolean isSimplePropertySetter(@Nullable PsiMethod method) {
if (method == null) return false;
if (method.isConstructor()) return false;
String methodName = method.getName();
if (!isSetterName(methodName)) return false;
if (method.getParameterList().getParametersCount() != 1) {
return false;
}
final PsiType returnType = method.getReturnType();
if (returnType == null || PsiType.VOID.equals(returnType)) {
return true;
}
return Comparing.equal(PsiUtil.resolveClassInType(TypeConversionUtil.erasure(returnType)), method.getContainingClass());
}
public static boolean isSetterName(@NotNull String methodName) {
return checkPrefix(methodName, "set");
}
@Nullable
public static String getPropertyName(@NonNls @NotNull String methodName) {
return StringUtil.getPropertyName(methodName);
public static String getPropertyName(@NotNull PsiMethod method) {
if (isSimplePropertyGetter(method)) {
return getPropertyNameByGetter(method);
}
if (isSimplePropertySetter(method)) {
return getPropertyNameBySetter(method);
}
return null;
}
@NotNull
public static String getPropertyNameByGetter(PsiMethod getterMethod) {
@NonNls String methodName = getterMethod.getName();
if (methodName.startsWith("get")) return StringUtil.decapitalize(methodName.substring(3));
if (methodName.startsWith("is")) return StringUtil.decapitalize(methodName.substring(2));
return methodName;
}
@NotNull
public static String getPropertyNameBySetter(@NotNull PsiMethod setterMethod) {
String methodName = setterMethod.getName();
return Introspector.decapitalize(methodName.substring(3));
}
private static boolean checkPrefix(@NotNull String methodName, @NotNull String prefix) {
boolean hasPrefix = methodName.startsWith(prefix) && methodName.length() > prefix.length();
return hasPrefix && !(Character.isLowerCase(methodName.charAt(prefix.length())) &&
(methodName.length() == prefix.length() + 1 || Character.isLowerCase(methodName.charAt(prefix.length() + 1))));
}
@NonNls
@NotNull
public static String[] suggestGetterNames(@NotNull String propertyName) {
final String str = StringUtil.capitalizeWithJavaBeanConvention(StringUtil.sanitizeJavaIdentifier(propertyName));
return new String[]{IS_PREFIX + str, "get" + str};
}
public static String suggestGetterName(@NonNls @NotNull String propertyName, @Nullable PsiType propertyType) {
@@ -326,16 +440,6 @@ public class PropertyUtil {
return name.toString();
}
private static boolean isBoolean(@Nullable PsiType propertyType) {
return PsiType.BOOLEAN.equals(propertyType);
}
@NonNls
@NotNull
public static String[] suggestGetterNames(@NotNull String propertyName) {
final String str = StringUtil.capitalizeWithJavaBeanConvention(StringUtil.sanitizeJavaIdentifier(propertyName));
return new String[]{IS_PREFIX + str, "get" + str};
}
public static String suggestSetterName(@NonNls @NotNull String propertyName) {
return suggestSetterName(propertyName, "set");
@@ -351,40 +455,6 @@ public class PropertyUtil {
return name.toString();
}
@NotNull
public static String[] getReadableProperties(@NotNull PsiClass aClass, boolean includeSuperClass) {
List<String> result = new ArrayList<>();
PsiMethod[] methods = includeSuperClass ? aClass.getAllMethods() : aClass.getMethods();
for (PsiMethod method : methods) {
if (CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) continue;
if (isSimplePropertyGetter(method)) {
result.add(getPropertyName(method));
}
}
return ArrayUtil.toStringArray(result);
}
@NotNull
public static String[] getWritableProperties(@NotNull PsiClass aClass, boolean includeSuperClass) {
List<String> result = new ArrayList<>();
PsiMethod[] methods = includeSuperClass ? aClass.getAllMethods() : aClass.getMethods();
for (PsiMethod method : methods) {
if (CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName())) continue;
if (isSimplePropertySetter(method)) {
result.add(getPropertyName(method));
}
}
return ArrayUtil.toStringArray(result);
}
/**
* Consider using {@link com.intellij.codeInsight.generation.GenerateMembersUtil#generateGetterPrototype(com.intellij.psi.PsiField)} or
* {@link com.intellij.codeInsight.generation.GenerateMembersUtil#generateSimpleGetterPrototype(com.intellij.psi.PsiField)}
@@ -498,61 +568,6 @@ public class PropertyUtil {
NullableNotNullManager.getInstance(field.getProject()).copyNullableOrNotNullAnnotation(field, listOwner);
}
public static String suggestPropertyName(@NotNull PsiField field) {
return suggestPropertyName(field, field.getName());
}
@NotNull
public static String suggestPropertyName(@NotNull PsiField field, @NotNull String fieldName) {
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(field.getProject());
VariableKind kind = codeStyleManager.getVariableKind(field);
String name = codeStyleManager.variableNameToPropertyName(fieldName, kind);
if (!field.hasModifierProperty(PsiModifier.STATIC) && isBoolean(field.getType())) {
if (name.startsWith(IS_PREFIX) && name.length() > IS_PREFIX.length() && Character.isUpperCase(name.charAt(IS_PREFIX.length()))) {
name = Introspector.decapitalize(name.substring(IS_PREFIX.length()));
}
}
return name;
}
public static String suggestGetterName(PsiField field) {
String propertyName = suggestPropertyName(field);
return suggestGetterName(propertyName, field.getType());
}
public static String suggestSetterName(PsiField field) {
String propertyName = suggestPropertyName(field);
return suggestSetterName(propertyName);
}
@Nullable
public static String getPropertyName(final PsiMember member) {
if (member instanceof PsiMethod) {
return getPropertyName((PsiMethod)member);
}
if (member instanceof PsiField) {
return member.getName();
}
return null;
}
@Nullable
public static PsiType getPropertyType(final PsiMember member) {
if (member instanceof PsiField) {
return ((PsiField)member).getType();
}
if (member instanceof PsiMethod) {
final PsiMethod psiMethod = (PsiMethod)member;
if (isSimplePropertyGetter(psiMethod)) {
return psiMethod.getReturnType();
}
else if (isSimplePropertySetter(psiMethod)) {
return psiMethod.getParameterList().getParameters()[0].getType();
}
}
return null;
}
@Nullable
public static PsiTypeElement getPropertyTypeElement(final PsiMember member) {
if (member instanceof PsiField) {
@@ -633,7 +648,7 @@ public class PropertyUtil {
* returns the returned expression. Otherwise, returns null.
*
* @param method the method to check
* @return the return value, or null if it doesn't match the condotions.
* @return the return value, or null if it doesn't match the conditions.
*/
@Nullable
public static PsiExpression getGetterReturnExpression(PsiMethod method) {
@@ -655,201 +670,4 @@ public class PropertyUtil {
return statement instanceof PsiReturnStatement ? ((PsiReturnStatement)statement).getReturnValue() : null;
}
@Nullable
public static PsiField getFieldOfGetter(PsiMethod method) {
PsiField field = getSimplyReturnedField(method, getGetterReturnExpression(method));
if (field != null) {
final PsiType returnType = method.getReturnType();
if (returnType != null && field.getType().equalsToText(returnType.getCanonicalText())) {
return field;
}
}
return null;
}
@Nullable
public static PsiField getSimplyReturnedField(PsiMethod method, @Nullable PsiExpression value) {
if (!(value instanceof PsiReferenceExpression)) {
return null;
}
final PsiReferenceExpression reference = (PsiReferenceExpression)value;
if (hasSubstantialQualifier(reference)) {
return null;
}
final PsiElement referent = reference.resolve();
if (!(referent instanceof PsiField)) {
return null;
}
final PsiField field = (PsiField)referent;
return InheritanceUtil.isInheritorOrSelf(method.getContainingClass(), field.getContainingClass(), true) ? field : null;
}
private static boolean hasSubstantialQualifier(PsiReferenceExpression reference) {
final PsiExpression qualifier = reference.getQualifierExpression();
if (qualifier == null) return false;
if (qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) {
return false;
}
if (qualifier instanceof PsiReferenceExpression) {
return !(((PsiReferenceExpression)qualifier).resolve() instanceof PsiClass);
}
return true;
}
public static boolean isSimpleGetter(PsiMethod method) {
return getFieldOfGetter(method) != null;
}
@Nullable
public static PsiField getFieldOfSetter(PsiMethod method) {
if (method == null) {
return null;
}
final PsiParameterList parameterList = method.getParameterList();
if (parameterList.getParametersCount() != 1) {
return null;
}
@NonNls final String name = method.getName();
if (!name.startsWith("set")) {
return null;
}
if (method.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {
return null;
}
final PsiCodeBlock body = method.getBody();
if (body == null) {
return null;
}
final PsiStatement[] statements = body.getStatements();
if (statements.length != 1) {
return null;
}
final PsiStatement statement = statements[0];
if (!(statement instanceof PsiExpressionStatement)) {
return null;
}
final PsiExpressionStatement possibleAssignmentStatement = (PsiExpressionStatement)statement;
final PsiExpression possibleAssignment = possibleAssignmentStatement.getExpression();
if (!(possibleAssignment instanceof PsiAssignmentExpression)) {
return null;
}
final PsiAssignmentExpression assignment = (PsiAssignmentExpression)possibleAssignment;
if (!JavaTokenType.EQ.equals(assignment.getOperationTokenType())) {
return null;
}
final PsiExpression lhs = assignment.getLExpression();
if (!(lhs instanceof PsiReferenceExpression)) {
return null;
}
final PsiReferenceExpression reference = (PsiReferenceExpression)lhs;
final PsiExpression qualifier = reference.getQualifierExpression();
if (qualifier instanceof PsiReferenceExpression) {
final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier;
final PsiElement target = referenceExpression.resolve();
if (!(target instanceof PsiClass)) {
return null;
}
}
else if (qualifier != null && !(qualifier instanceof PsiThisExpression) && !(qualifier instanceof PsiSuperExpression)) {
return null;
}
final PsiElement referent = reference.resolve();
if (referent == null) {
return null;
}
if (!(referent instanceof PsiField)) {
return null;
}
final PsiField field = (PsiField)referent;
final PsiClass fieldContainingClass = field.getContainingClass();
final PsiClass methodContainingClass = method.getContainingClass();
if (!InheritanceUtil.isInheritorOrSelf(methodContainingClass, fieldContainingClass, true)) {
return null;
}
final PsiExpression rhs = assignment.getRExpression();
if (!(rhs instanceof PsiReferenceExpression)) {
return null;
}
final PsiReferenceExpression rReference = (PsiReferenceExpression)rhs;
final PsiExpression rQualifier = rReference.getQualifierExpression();
if (rQualifier != null) {
return null;
}
final PsiElement rReferent = rReference.resolve();
if (rReferent == null) {
return null;
}
if (!(rReferent instanceof PsiParameter)) {
return null;
}
final PsiType fieldType = field.getType();
final PsiType parameterType = ((PsiVariable)rReferent).getType();
if (fieldType.equalsToText(parameterType.getCanonicalText())) {
return field;
}
return null;
}
public static boolean isSimpleSetter(PsiMethod method) {
return getFieldOfSetter(method) != null;
}
@Nullable
public static PsiMethod getReversePropertyMethod(PsiMethod propertyMethod) {
if (propertyMethod == null) {
return null;
}
final PsiClass aClass = propertyMethod.getContainingClass();
if (aClass == null) {
return null;
}
final String methodName = propertyMethod.getName();
final String prefix;
if (methodName.startsWith("get")) {
prefix = "get";
}
else if (methodName.startsWith(IS_PREFIX)) {
prefix = IS_PREFIX;
}
else if (methodName.startsWith("set")) {
prefix = "set";
}
else {
return null;
}
final String name = methodName.substring(prefix.length());
final PsiField field = prefix.equals("set") ? getFieldOfSetter(propertyMethod) : getFieldOfGetter(propertyMethod);
if (field == null) {
return null;
}
if (prefix.equals("set")) {
final PsiMethod result = findPropertyMethod(aClass, "get", name, field);
if (result != null) {
return result;
}
return findPropertyMethod(aClass, IS_PREFIX, name, field);
}
else {
return findPropertyMethod(aClass, "set", name, field);
}
}
private static PsiMethod findPropertyMethod(@NotNull PsiClass aClass,
@NotNull String prefix,
@NotNull String propertyName,
@NotNull PsiField field1) {
final PsiMethod[] methods = aClass.findMethodsByName(prefix + propertyName, true);
for (PsiMethod method : methods) {
final PsiField field2 = prefix.equals("set") ? getFieldOfSetter(method) : getFieldOfGetter(method);
if (field1.equals(field2)) {
return method;
}
}
return null;
}
}

View File

@@ -39,10 +39,7 @@ import com.intellij.psi.impl.source.tree.JavaDocElementType;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.util.*;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -108,7 +105,7 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem
if (statements.length == 0) return false;
PsiStatement statement = statements[0];
if (PropertyUtil.isSimplePropertyGetter(method)) {
if (PropertyUtilBase.isSimplePropertyGetter(method)) {
if (statement instanceof PsiReturnStatement) {
return ((PsiReturnStatement)statement).getReturnValue() instanceof PsiReferenceExpression;
}
@@ -127,7 +124,7 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem
return lhs instanceof PsiReferenceExpression &&
rhs instanceof PsiReferenceExpression &&
!((PsiReferenceExpression)rhs).isQualified() &&
PropertyUtil.isSimplePropertySetter(method); // last check because it can perform long return type resolve
PropertyUtilBase.isSimplePropertySetter(method); // last check because it can perform long return type resolve
}
}
return false;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -16,7 +16,7 @@
package com.intellij.codeInsight.highlighting;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PropertyUtilBase;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
@@ -61,7 +61,7 @@ public class JavaReadWriteAccessDetector extends ReadWriteAccessDetector {
if (!writeAccess && expr instanceof PsiReferenceExpression) {
//when searching usages of fields, should show all found setters as a "only write usage"
PsiElement actualReferee = ((PsiReferenceExpression) expr).resolve();
if (actualReferee instanceof PsiMethod && PropertyUtil.isSimplePropertySetter((PsiMethod)actualReferee)) {
if (actualReferee instanceof PsiMethod && PropertyUtilBase.isSimplePropertySetter((PsiMethod)actualReferee)) {
writeAccess = true;
readAccess = false;
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2000-2017 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.psi.stub;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.PsiMethodImpl;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.stubs.StubTree;
import com.intellij.util.ArrayUtil;
public class JavaStubImplUtil {
public static int getMethodStubIndex(PsiMethod method) {
if (!(method instanceof PsiMethodImpl)) return -1;
PsiFileImpl file = (PsiFileImpl)method.getContainingFile();
if (file.getElementTypeForStubBuilder() == null) return -1;
StubTree stubTree = file.getStubTree();
if (stubTree == null) {
stubTree = file.calcStubTree();
}
PsiElement[] stubs = stubTree
.getPlainList()
.stream()
.filter(e -> e.getStubType() == JavaElementType.METHOD)
.map(StubElement::getPsi)
.toArray(PsiElement[]::new);
return ArrayUtil.indexOf(stubs, method);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -28,7 +28,7 @@ import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PropertyUtilBase;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -65,7 +65,7 @@ public class PropertyGroup implements Group, ColoredItemPresentation, AccessLeve
public static PropertyGroup createOn(PsiElement object, final TreeElement treeElement) {
if (object instanceof PsiField) {
PsiField field = (PsiField)object;
PropertyGroup group = new PropertyGroup(PropertyUtil.suggestPropertyName(field), field.getType(),
PropertyGroup group = new PropertyGroup(PropertyUtilBase.suggestPropertyName(field), field.getType(),
field.hasModifierProperty(PsiModifier.STATIC), object.getProject());
group.setField(field);
group.myChildren.add(treeElement);
@@ -73,16 +73,16 @@ public class PropertyGroup implements Group, ColoredItemPresentation, AccessLeve
}
else if (object instanceof PsiMethod) {
PsiMethod method = (PsiMethod)object;
if (PropertyUtil.isSimplePropertyGetter(method)) {
PropertyGroup group = new PropertyGroup(PropertyUtil.getPropertyNameByGetter(method), method.getReturnType(),
if (PropertyUtilBase.isSimplePropertyGetter(method)) {
PropertyGroup group = new PropertyGroup(PropertyUtilBase.getPropertyNameByGetter(method), method.getReturnType(),
method.hasModifierProperty(PsiModifier.STATIC), object.getProject());
group.setGetter(method);
group.myChildren.add(treeElement);
return group;
}
else if (PropertyUtil.isSimplePropertySetter(method)) {
else if (PropertyUtilBase.isSimplePropertySetter(method)) {
PropertyGroup group =
new PropertyGroup(PropertyUtil.getPropertyNameBySetter(method), method.getParameterList().getParameters()[0].getType(),
new PropertyGroup(PropertyUtilBase.getPropertyNameBySetter(method), method.getParameterList().getParameters()[0].getType(),
method.hasModifierProperty(PsiModifier.STATIC), object.getProject());
group.setSetter(method);
group.myChildren.add(treeElement);

View File

@@ -13,8 +13,6 @@ class Editor {
}
class Main {
void stats() {
Editor e = <caret>
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2000-2017 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.util
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.psi.PsiMethod
import com.intellij.psi.util.PropertyMemberType
import com.intellij.psi.util.PropertyUtil
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import kotlin.test.assertNotEquals
class JavaPropertyDetectionTest : LightCodeInsightFixtureTestCase() {
// getter field test
fun testFieldRef() {
assertPropertyMember("""class Some {private String name;public String getN<caret>ame() { return name; }}""", PropertyMemberType.GETTER)
}
fun testUnresolvedRef() {
assertNotPropertyMember("class Some { public String getN<caret>ame() { return name; }}", PropertyMemberType.GETTER)
}
fun testUnresolvedRef2() {
assertNotPropertyMember("""class Some {private String name;public static String getN<caret>ame() { return name; }}""", PropertyMemberType.GETTER)
}
fun testSuperFieldRef() {
doTest("""class Some extends SomeBase {public String getN<caret>ame() { return name; }}class SomeBase { private String name;}""", PropertyMemberType.GETTER,
false)
}
fun testSuperFieldRef2() {
assertPropertyMember("""class Some extends SomeBase
|{public String getN<caret>ame() { return name; }}class SomeBase { protected String name;}""".trimMargin(), PropertyMemberType.GETTER)
}
fun testInconsistentType() {
assertNotPropertyMember("""class Some
|{String name;public long getNa<caret>me() { return name;}}""".trimMargin(), PropertyMemberType.GETTER)
}
fun testWithSuperKeyWord() {
assertPropertyMember("""import java.util.List; class Some extends SomeBase
|{ List<String> getL<caret>ist() { return Some.super.list;}} class SomeBase {List<String> list;} """.trimMargin(), PropertyMemberType.GETTER)
}
fun testRedundantParenthesises() {
assertNotPropertyMember("""import java.util.List;
class Some {
Some a;
List<String> list;
List<String> getL<caret>ist() {return (a).list;}
}""", PropertyMemberType.GETTER)
}
// setter field test
fun testSimpleSetter() {
assertPropertyMember("""class Some {
|private String name; public void set<caret>Name(String name) { this.name = name; }}""".trimMargin(), PropertyMemberType.SETTER)
}
fun testFieldNotResolved() {
assertNotPropertyMember("""class Some {
| public void set<caret>Name(String name) { this.name = name; }}""".trimMargin(), PropertyMemberType.SETTER)
}
fun testInvalidExpression() {
assertNotPropertyMember("""class Some {
|public void set<caret>Name(String name) { = name; }}""".trimMargin(), PropertyMemberType.SETTER)
}
fun testInvalidReference() {
assertNotPropertyMember("""class Some {
|public void set<caret>Name(String name) { name = name; }}""".trimMargin(), PropertyMemberType.SETTER)
}
fun testSuperClassField() {
assertPropertyMember( """class Some extends SomeBase {
| public void set<caret>Name(String name) { this.name = name; }}
| class SomeBase {protected String name;}""".trimMargin(), PropertyMemberType.SETTER)
}
fun testRefWithSuperKeyword() {
assertPropertyMember( """class Some extends SomeBase {
|public void set<caret>Name(String name) { Some.super.name = name; }}
| class SomeBase {protected String name;}""".trimMargin(), PropertyMemberType.SETTER)
}
fun testOuterClassFieldSetter() {
assertNotPropertyMember("""class Outer {String name; class Some {
|public void set<caret>Name(String name) { this.name = name; }}}""".trimMargin(), PropertyMemberType.SETTER)
}
private fun assertPropertyMember(text: String, memberType: PropertyMemberType) {
doTest(text, memberType, true)
}
private fun assertNotPropertyMember(text: String, memberType: PropertyMemberType) {
doTest(text, memberType, false)
}
private fun doTest(text: String, memberType: PropertyMemberType, expectedDecision: Boolean) {
assertNotEquals(PropertyMemberType.FIELD, memberType)
myFixture.configureByText(JavaFileType.INSTANCE, text)
val method = PsiTreeUtil.getNonStrictParentOfType(myFixture.elementAtCaret, PsiMethod::class.java)
assertNotNull(method)
//use index
assertEquals(expectedDecision, if (memberType == PropertyMemberType.GETTER) PropertyUtil.isSimpleGetter(method) else PropertyUtil.isSimpleSetter(method))
//use ast
assertEquals(expectedDecision, if (memberType == PropertyMemberType.GETTER) PropertyUtil.isSimpleGetter(method, false) else PropertyUtil.isSimpleSetter(method, false))
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -24,7 +24,7 @@ import com.intellij.ide.TypePresentationService;
import com.intellij.psi.*;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.util.PropertyMemberType;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PropertyUtilBase;
import com.intellij.util.xml.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -67,11 +67,11 @@ public abstract class AbstractMemberResolveConverter extends ResolvingConverter<
if (field != null) return field;
break;
case GETTER:
final PsiMethod getter = PropertyUtil.findPropertyGetter(psiClass, getPropertyName(s, context), false, isLookDeep());
final PsiMethod getter = PropertyUtilBase.findPropertyGetter(psiClass, getPropertyName(s, context), false, isLookDeep());
if (getter != null) return getter;
break;
case SETTER:
final PsiMethod setter = PropertyUtil.findPropertySetter(psiClass, getPropertyName(s, context), false, isLookDeep());
final PsiMethod setter = PropertyUtilBase.findPropertySetter(psiClass, getPropertyName(s, context), false, isLookDeep());
if (setter != null) return setter;
break;
}
@@ -110,7 +110,7 @@ public abstract class AbstractMemberResolveConverter extends ResolvingConverter<
}
protected boolean methodSuits(final PsiMethod psiMethod) {
return !psiMethod.isConstructor() && !psiMethod.hasModifierProperty(PsiModifier.STATIC) && PropertyUtil.getPropertyName(psiMethod) != null;
return !psiMethod.isConstructor() && !psiMethod.hasModifierProperty(PsiModifier.STATIC) && PropertyUtilBase.getPropertyName(psiMethod) != null;
}
protected boolean fieldSuits(final PsiField psiField) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -19,7 +19,7 @@ import com.intellij.codeInsight.completion.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtil;
import com.intellij.psi.util.PropertyUtilBase;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.psi.*;
@@ -62,8 +62,8 @@ public class PyConstructorArgumentCompletionContributor extends CompletionContri
// see PyJavaType.init() in Jython source code for matching logic
for (PsiMethod method : containingClass.getAllMethods()) {
final Project project = containingClass.getProject();
if (PropertyUtil.isSimplePropertySetter(method)) {
final String propName = PropertyUtil.getPropertyName(method);
if (PropertyUtilBase.isSimplePropertySetter(method)) {
final String propName = PropertyUtilBase.getPropertyName(method);
result.addElement(PyUtil.createNamedParameterLookup(propName, project));
}
else if (method.getName().startsWith("add") && method.getName().endsWith("Listener") && PsiType.VOID.equals(method.getReturnType())) {

View File

@@ -1769,6 +1769,8 @@
<fileBasedIndex implementation="com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisIndex"/>
<fileBasedIndex implementation="com.intellij.psi.RefQueueIndex"/>
<fileBasedIndex implementation="com.intellij.psi.impl.java.stubs.index.JavaAutoModuleNameIndex"/>
<fileBasedIndex implementation="com.intellij.psi.impl.search.JavaNullMethodArgumentIndex"/>
<fileBasedIndex implementation="com.intellij.psi.impl.JavaSimplePropertyIndex"/>
<stubElementTypeHolder class="com.intellij.psi.impl.java.stubs.JavaStubElementTypes"/>
@@ -1993,8 +1995,6 @@
<refactoring.pushDown language="JAVA" implementationClass="com.intellij.refactoring.memberPushDown.JavaPushDownDelegate" id="java"/>
<library.javaSourceRootDetector implementation="com.intellij.openapi.roots.ui.configuration.LibraryJavaSourceRootDetector"/>
<fileBasedIndex implementation="com.intellij.psi.impl.search.JavaNullMethodArgumentIndex"/>
<codeUsageScopeOptimizer implementation="com.intellij.compiler.JavaCompilerReferencesCodeUsageScopeOptimizer"/>
<importTestOutput implementation="com.intellij.execution.AntTestContentHandler$AntTestOutputExtension"/>