Move MethodReferencesSearch (and necessary closure) to java-indexing-api.

Remove references to StfFileTypes from SimpleAccessorReferencesSearch and extract them into one more CustomPropertyScopeProvider
This commit is contained in:
Evgeny Pasynkov
2012-07-30 11:24:30 +02:00
parent 78fc86e7db
commit 3944360e63
16 changed files with 34 additions and 7 deletions
+1
View File
@@ -8,6 +8,7 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="core-api" exported="" />
<orderEntry type="module" module-name="projectModel-api" />
</component>
</module>
@@ -0,0 +1,420 @@
/*
* Copyright 2000-2009 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.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author max
*/
public class AnnotationUtil {
/**
* The full qualified name of the standard Nullable annotation.
*/
public static final String NULLABLE = "org.jetbrains.annotations.Nullable";
/**
* The full qualified name of the standard NotNull annotation.
*/
public static final String NOT_NULL = "org.jetbrains.annotations.NotNull";
@NonNls public static final String NOT_NULL_SIMPLE_NAME = "NotNull";
@NonNls public static final String NULLABLE_SIMPLE_NAME = "Nullable";
/**
* The full qualified name of the standard NonNls annotation.
*
* @since 5.0.1
*/
public static final String NON_NLS = "org.jetbrains.annotations.NonNls";
public static final String NLS = "org.jetbrains.annotations.Nls";
public static final String PROPERTY_KEY = "org.jetbrains.annotations.PropertyKey";
@NonNls public static final String PROPERTY_KEY_RESOURCE_BUNDLE_PARAMETER = "resourceBundle";
@NonNls public static final String NON_NLS_SIMPLE_NAME = "NonNls";
@NonNls public static final String PROPERTY_KEY_SIMPLE_NAME = "PropertyKey";
public static final String TEST_ONLY = "org.jetbrains.annotations.TestOnly";
@NonNls public static final String TEST_ONLY_SIMPLE_NAME = "TestOnly";
public static final String LANGUAGE = "org.intellij.lang.annotations.Language";
public static final Set<String> ALL_ANNOTATIONS;
@NonNls private static final String[] SIMPLE_NAMES =
{NOT_NULL_SIMPLE_NAME, NULLABLE_SIMPLE_NAME, NON_NLS_SIMPLE_NAME, PROPERTY_KEY_SIMPLE_NAME, TEST_ONLY_SIMPLE_NAME,
"Language", "Identifier", "Pattern", "PrintFormat", "RegExp", "Subst"};
static {
ALL_ANNOTATIONS = new HashSet<String>(2);
ALL_ANNOTATIONS.add(NULLABLE);
ALL_ANNOTATIONS.add(NOT_NULL);
}
@Nullable
public static PsiAnnotation findAnnotation(PsiModifierListOwner listOwner, @NotNull String... annotationNames) {
return findAnnotation(listOwner, false, annotationNames);
}
@Nullable
public static PsiAnnotation findAnnotation(PsiModifierListOwner listOwner, final boolean skipExternal, @NotNull String... annotationNames) {
if (annotationNames.length == 0) return null;
Set<String> set = annotationNames.length == 1 ? Collections.singleton(annotationNames[0]) : new HashSet<String>(Arrays.asList(annotationNames));
return findAnnotation(listOwner, set, skipExternal);
}
@Nullable
public static PsiAnnotation findAnnotation(@Nullable PsiModifierListOwner listOwner, @NotNull Set<String> annotationNames) {
return findAnnotation(listOwner, (Collection<String>)annotationNames);
}
@Nullable
public static PsiAnnotation findAnnotation(@Nullable PsiModifierListOwner listOwner, Collection<String> annotationNames) {
return findAnnotation(listOwner, annotationNames, false);
}
@Nullable
public static PsiAnnotation findAnnotation(@Nullable PsiModifierListOwner listOwner, @NotNull Collection<String> annotationNames,
final boolean skipExternal) {
if (listOwner == null) return null;
final PsiModifierList list = listOwner.getModifierList();
if (list == null) return null;
final PsiAnnotation[] allAnnotations = list.getAnnotations();
for (PsiAnnotation annotation : allAnnotations) {
String qualifiedName = annotation.getQualifiedName();
if (annotationNames.contains(qualifiedName)) {
return annotation;
}
}
if (!skipExternal) {
final ExternalAnnotationsManager annotationsManager = ExternalAnnotationsManager.getInstance(listOwner.getProject());
for (String annotationName : annotationNames) {
final PsiAnnotation annotation = annotationsManager.findExternalAnnotation(listOwner, annotationName);
if (annotation != null) {
return annotation;
}
}
}
return null;
}
@NotNull
public static PsiAnnotation[] findAnnotations(final PsiModifierListOwner modifierListOwner, @NotNull Collection<String> annotationNames) {
if (modifierListOwner == null) return PsiAnnotation.EMPTY_ARRAY;
final PsiModifierList modifierList = modifierListOwner.getModifierList();
if (modifierList == null) return PsiAnnotation.EMPTY_ARRAY;
final PsiAnnotation[] annotations = modifierList.getAnnotations();
ArrayList<PsiAnnotation> result = null;
for (final PsiAnnotation psiAnnotation : annotations) {
if (annotationNames.contains(psiAnnotation.getQualifiedName())) {
if (result == null) result = new ArrayList<PsiAnnotation>();
result.add(psiAnnotation);
}
}
return result == null ? PsiAnnotation.EMPTY_ARRAY : result.toArray(new PsiAnnotation[result.size()]);
}
@Nullable
public static PsiAnnotation findAnnotationInHierarchy(PsiModifierListOwner listOwner, @NotNull Set<String> annotationNames) {
PsiAnnotation directAnnotation = findAnnotation(listOwner, annotationNames);
if (directAnnotation != null) return directAnnotation;
if (listOwner instanceof PsiMethod) {
PsiMethod method = (PsiMethod)listOwner;
PsiClass aClass = method.getContainingClass();
if (aClass == null) return null;
HierarchicalMethodSignature methodSignature = method.getHierarchicalMethodSignature();
return findAnnotationInHierarchy(methodSignature, annotationNames, method, null,
JavaPsiFacade.getInstance(method.getProject()).getResolveHelper());
}
if (listOwner instanceof PsiClass) {
return findAnnotationInHierarchy((PsiClass)listOwner, annotationNames, null);
}
if (listOwner instanceof PsiParameter) {
PsiParameter parameter = (PsiParameter)listOwner;
return doFindAnnotationInHierarchy(parameter, annotationNames, null);
}
return null;
}
@Nullable
private static PsiAnnotation doFindAnnotationInHierarchy(PsiParameter parameter,
Set<String> annotationNames,
@Nullable Set<PsiModifierListOwner> visited) {
PsiAnnotation annotation = findAnnotation(parameter, annotationNames);
if (annotation != null) return annotation;
PsiElement scope = parameter.getDeclarationScope();
if (!(scope instanceof PsiMethod)) {
return null;
}
PsiMethod method = (PsiMethod)scope;
PsiClass aClass = method.getContainingClass();
PsiElement parent = parameter.getParent();
if (aClass == null || !(parent instanceof PsiParameterList)) {
return null;
}
int index = ((PsiParameterList)parent).getParameterIndex(parameter);
HierarchicalMethodSignature methodSignature = method.getHierarchicalMethodSignature();
final List<HierarchicalMethodSignature> superSignatures = methodSignature.getSuperSignatures();
PsiResolveHelper resolveHelper = PsiResolveHelper.SERVICE.getInstance(aClass.getProject());
for (final HierarchicalMethodSignature superSignature : superSignatures) {
final PsiMethod superMethod = superSignature.getMethod();
if (visited == null) visited = new THashSet<PsiModifierListOwner>();
if (!visited.add(superMethod)) continue;
if (!resolveHelper.isAccessible(superMethod, parameter, null)) continue;
PsiParameter[] superParameters = superMethod.getParameterList().getParameters();
if (index < superParameters.length) {
PsiAnnotation insuper = doFindAnnotationInHierarchy(superParameters[index], annotationNames, visited);
if (insuper != null) return insuper;
}
}
return null;
}
@Nullable
private static PsiAnnotation findAnnotationInHierarchy(@NotNull final PsiClass psiClass, final Set<String> annotationNames, @Nullable Set<PsiClass> processed) {
final PsiClass[] superClasses = psiClass.getSupers();
for (final PsiClass superClass : superClasses) {
if (processed == null) processed = new THashSet<PsiClass>();
if (!processed.add(superClass)) return null;
final PsiAnnotation annotation = findAnnotation(superClass, annotationNames);
if (annotation != null) return annotation;
final PsiAnnotation annotationInHierarchy = findAnnotationInHierarchy(superClass, annotationNames, processed);
if (annotationInHierarchy != null) return annotationInHierarchy;
}
return null;
}
@Nullable
private static PsiAnnotation findAnnotationInHierarchy(@NotNull HierarchicalMethodSignature signature,
@NotNull Set<String> annotationNames,
@NotNull PsiElement place,
@Nullable Set<PsiMethod> processed,
@NotNull PsiResolveHelper resolveHelper) {
final List<HierarchicalMethodSignature> superSignatures = signature.getSuperSignatures();
for (final HierarchicalMethodSignature superSignature : superSignatures) {
final PsiMethod superMethod = superSignature.getMethod();
if (processed == null) processed = new THashSet<PsiMethod>();
if (!processed.add(superMethod)) continue;
if (!resolveHelper.isAccessible(superMethod, place, null)) continue;
PsiAnnotation direct = findAnnotation(superMethod, annotationNames);
if (direct != null) return direct;
PsiAnnotation superResult = findAnnotationInHierarchy(superSignature, annotationNames, place, processed, resolveHelper);
if (superResult != null) return superResult;
}
return null;
}
public static boolean isAnnotated(@NotNull PsiModifierListOwner listOwner, Collection<String> annotations) {
return isAnnotated(listOwner, annotations, false);
}
public static boolean isAnnotated(@NotNull PsiModifierListOwner listOwner,
Collection<String> annotations,
final boolean checkHierarchy) {
for (String annotation : annotations) {
if (isAnnotated(listOwner, annotation, checkHierarchy)) return true;
}
return false;
}
public static boolean isAnnotated(@NotNull PsiModifierListOwner listOwner, @NonNls String annotationFQN, boolean checkHierarchy) {
return isAnnotated(listOwner, annotationFQN, checkHierarchy, false, null);
}
public static boolean isAnnotated(@NotNull PsiModifierListOwner listOwner, @NonNls String annotationFQN, boolean checkHierarchy,
boolean skipExternal) {
return isAnnotated(listOwner, annotationFQN, checkHierarchy, skipExternal, null);
}
private static boolean isAnnotated(@NotNull PsiModifierListOwner listOwner,
@NonNls String annotationFQN,
boolean checkHierarchy, final boolean skipExternal, @Nullable Set<PsiMember> processed) {
if (!listOwner.isValid()) return false;
final PsiModifierList modifierList = listOwner.getModifierList();
if (modifierList == null) return false;
PsiAnnotation annotation = modifierList.findAnnotation(annotationFQN);
if (annotation != null) return true;
if (!skipExternal && ExternalAnnotationsManager.getInstance(listOwner.getProject()).findExternalAnnotation(listOwner, annotationFQN) != null) {
return true;
}
if (checkHierarchy) {
if (listOwner instanceof PsiMethod) {
PsiMethod method = (PsiMethod)listOwner;
if (processed == null) processed = new THashSet<PsiMember>();
if (!processed.add(method)) return false;
final PsiMethod[] superMethods = method.findSuperMethods();
for (PsiMethod superMethod : superMethods) {
if (isAnnotated(superMethod, annotationFQN, checkHierarchy, skipExternal, processed)) return true;
}
} else if (listOwner instanceof PsiClass) {
final PsiClass clazz = (PsiClass)listOwner;
if (processed == null) processed = new THashSet<PsiMember>();
if (!processed.add(clazz)) return false;
final PsiClass[] superClasses = clazz.getSupers();
for (PsiClass superClass : superClasses) {
if (isAnnotated(superClass, annotationFQN, checkHierarchy, skipExternal, processed)) return true;
}
}
}
return false;
}
public static boolean isAnnotatingApplicable(@NotNull PsiElement elt) {
final Project project = elt.getProject();
return PsiUtil.isLanguageLevel5OrHigher(elt) &&
JavaPsiFacade.getInstance(project).findClass(NullableNotNullManager.getInstance(project).getDefaultNullable(), elt.getResolveScope()) != null;
}
public static boolean isJetbrainsAnnotation(@NonNls final String simpleName) {
return ArrayUtil.find(SIMPLE_NAMES, simpleName) != -1;
}
/**
* Works similar to #isAnnotated(PsiModifierListOwner, Collection<String>) but supports FQN patters
* like "javax.ws.rs.*". Supports ending "*" only.
*
* @param owner modifier list
* @param annotations annotations qualified names or patterns. Patterns can have '*' at the end
* @return <code>true</code> if annotated of at least one annotation from the annotations list
*/
public static boolean checkAnnotatedUsingPatterns(PsiModifierListOwner owner, Collection<String> annotations) {
final PsiModifierList modList;
if (owner == null || (modList = owner.getModifierList()) == null) return false;
List<String> fqns = null;
for (String fqn : annotations) {
boolean isPattern = fqn.endsWith("*");
if (!isPattern && isAnnotated(owner, fqn, false)) {
return true;
} else if (isPattern) {
if (fqns == null) {
fqns = new ArrayList<String>();
final PsiAnnotation[] annos = modList.getAnnotations();
for (PsiAnnotation anno : annos) {
final String qName = anno.getQualifiedName();
if (qName != null) {
fqns.add(qName);
}
}
if (fqns.isEmpty()) return false;
}
fqn = fqn.substring(0, fqn.length() - 2);
for (String annoFQN : fqns) {
if (annoFQN.startsWith(fqn)) {
return true;
}
}
}
}
return false;
}
@Nullable
public static PsiMethod getAnnotationMethod(PsiNameValuePair pair) {
final PsiAnnotation annotation = PsiTreeUtil.getParentOfType(pair.getParent(), PsiAnnotation.class);
assert annotation != null;
final String fqn = annotation.getQualifiedName();
if (fqn == null) return null;
final PsiClass psiClass = JavaPsiFacade.getInstance(pair.getProject()).findClass(fqn, pair.getResolveScope());
if (psiClass != null && psiClass.isAnnotationType()) {
final String name = pair.getName();
return ArrayUtil.getFirstElement(psiClass.findMethodsByName(name != null ? name : "value", false));
}
return null;
}
@NotNull
public static PsiAnnotation[] getAllAnnotations(@NotNull PsiModifierListOwner owner, boolean inHierarchy, Set<PsiModifierListOwner> visited) {
final PsiModifierList list = owner.getModifierList();
PsiAnnotation[] annotations = PsiAnnotation.EMPTY_ARRAY;
if (list != null) {
annotations = list.getAnnotations();
}
final PsiAnnotation[] externalAnnotations = ExternalAnnotationsManager.getInstance(owner.getProject()).findExternalAnnotations(owner);
if (externalAnnotations != null) {
annotations = ArrayUtil.mergeArrays(annotations, externalAnnotations, PsiAnnotation.ARRAY_FACTORY);
}
if (inHierarchy) {
if (owner instanceof PsiClass) {
for (PsiClass superClass : ((PsiClass)owner).getSupers()) {
if (visited == null) visited = new THashSet<PsiModifierListOwner>();
if (visited.add(superClass)) annotations = ArrayUtil.mergeArrays(annotations, getAllAnnotations(superClass, inHierarchy, visited));
}
}
else if (owner instanceof PsiMethod) {
PsiMethod method = (PsiMethod)owner;
PsiClass aClass = method.getContainingClass();
if (aClass != null) {
HierarchicalMethodSignature methodSignature = method.getHierarchicalMethodSignature();
final List<HierarchicalMethodSignature> superSignatures = methodSignature.getSuperSignatures();
PsiResolveHelper resolveHelper = PsiResolveHelper.SERVICE.getInstance(aClass.getProject());
for (final HierarchicalMethodSignature superSignature : superSignatures) {
final PsiMethod superMethod = superSignature.getMethod();
if (visited == null) visited = new THashSet<PsiModifierListOwner>();
if (!visited.add(superMethod)) continue;
if (!resolveHelper.isAccessible(superMethod, owner, null)) continue;
annotations = ArrayUtil.mergeArrays(annotations, getAllAnnotations(superMethod, inHierarchy, visited));
}
}
}
else if (owner instanceof PsiParameter) {
PsiParameter parameter = (PsiParameter)owner;
PsiElement scope = parameter.getDeclarationScope();
if (scope instanceof PsiMethod) {
PsiMethod method = (PsiMethod)scope;
PsiClass aClass = method.getContainingClass();
PsiElement parent = parameter.getParent();
if (aClass != null && parent instanceof PsiParameterList) {
int index = ((PsiParameterList)parent).getParameterIndex(parameter);
HierarchicalMethodSignature methodSignature = method.getHierarchicalMethodSignature();
final List<HierarchicalMethodSignature> superSignatures = methodSignature.getSuperSignatures();
PsiResolveHelper resolveHelper = PsiResolveHelper.SERVICE.getInstance(aClass.getProject());
for (final HierarchicalMethodSignature superSignature : superSignatures) {
final PsiMethod superMethod = superSignature.getMethod();
if (visited == null) visited = new THashSet<PsiModifierListOwner>();
if (!visited.add(superMethod)) continue;
if (!resolveHelper.isAccessible(superMethod, owner, null)) continue;
PsiParameter[] superParameters = superMethod.getParameterList().getParameters();
if (index < superParameters.length) {
annotations = ArrayUtil.mergeArrays(annotations, getAllAnnotations(superParameters[index], inHierarchy, visited));
}
}
}
}
}
}
return annotations;
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2009 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.
*/
/*
* User: anna
* Date: 26-Jun-2007
*/
package com.intellij.codeInsight;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NotNullLazyKey;
import com.intellij.psi.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class ExternalAnnotationsManager {
@NonNls public static final String ANNOTATIONS_XML = "annotations.xml";
public enum AnnotationPlace {
IN_CODE,
EXTERNAL,
NOWHERE
}
private static final NotNullLazyKey<ExternalAnnotationsManager, Project> INSTANCE_KEY = ServiceManager.createLazyKey(ExternalAnnotationsManager.class);
public static ExternalAnnotationsManager getInstance(@NotNull Project project) {
return INSTANCE_KEY.getValue(project);
}
@Nullable
public abstract PsiAnnotation findExternalAnnotation(@NotNull PsiModifierListOwner listOwner, @NotNull String annotationFQN);
@Nullable
public abstract PsiAnnotation[] findExternalAnnotations(@NotNull PsiModifierListOwner listOwner);
public abstract void annotateExternally(@NotNull PsiModifierListOwner listOwner,
@NotNull String annotationFQName,
@NotNull PsiFile fromFile,
PsiNameValuePair[] value);
public abstract boolean deannotate(@NotNull PsiModifierListOwner listOwner, @NotNull String annotationFQN);
public abstract AnnotationPlace chooseAnnotationsPlace(@NotNull PsiElement element);
}
@@ -0,0 +1,206 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight;
import com.intellij.openapi.components.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizableStringList;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.PsiModifierListOwner;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* User: anna
* Date: 1/25/11
*/
@State(
name = "NullableNotNullManager",
storages = {@Storage( file = StoragePathMacros.PROJECT_FILE)}
)
public class NullableNotNullManager implements PersistentStateComponent<Element> {
private static final Logger LOG = Logger.getInstance("#" + NullableNotNullManager.class.getName());
public String myDefaultNullable = AnnotationUtil.NULLABLE;
public String myDefaultNotNull = AnnotationUtil.NOT_NULL;
public final JDOMExternalizableStringList myNullables = new JDOMExternalizableStringList();
public final JDOMExternalizableStringList myNotNulls = new JDOMExternalizableStringList();
public static final String[] DEFAULT_NULLABLES = {AnnotationUtil.NULLABLE, "javax.annotation.Nullable", "edu.umd.cs.findbugs.annotations.Nullable"};
public static final String[] DEFAULT_NOT_NULLS = {AnnotationUtil.NOT_NULL, "javax.annotation.Nonnull", "edu.umd.cs.findbugs.annotations.NonNull"};
private static final Object LOCK = new Object();
public static NullableNotNullManager getInstance(Project project) {
return ServiceManager.getService(project, NullableNotNullManager.class);
}
public Collection<String> getAllAnnotations() {
final List<String> all = new ArrayList<String>(getNullables());
all.addAll(getNotNulls());
return all;
}
private static void addAllIfNotPresent(Collection<String> collection, String... annotations) {
for (String annotation : annotations) {
LOG.assertTrue(annotation != null);
if (!collection.contains(annotation)) {
collection.add(annotation);
}
}
}
public void setNotNulls(String[] annotations) {
myNotNulls.clear();
addAllIfNotPresent(myNotNulls, DEFAULT_NOT_NULLS);
addAllIfNotPresent(myNotNulls, annotations);
}
public void setNullables(String[] annotations) {
myNullables.clear();
addAllIfNotPresent(myNullables, DEFAULT_NULLABLES);
addAllIfNotPresent(myNullables, annotations);
}
public String getDefaultNullable() {
return myDefaultNullable;
}
@Nullable
public String getNullable(PsiModifierListOwner owner) {
for (String nullable : getNullables()) {
if (AnnotationUtil.isAnnotated(owner, nullable, false)) return nullable;
}
return null;
}
public void setDefaultNullable(@NotNull String defaultNullable) {
LOG.assertTrue(getNullables().contains(defaultNullable));
myDefaultNullable = defaultNullable;
}
public String getDefaultNotNull() {
return myDefaultNotNull;
}
@Nullable
public String getNotNull(PsiModifierListOwner owner) {
for (String notNull : getNotNulls()) {
if (AnnotationUtil.isAnnotated(owner, notNull, false)) return notNull;
}
return null;
}
public void setDefaultNotNull(@NotNull String defaultNotNull) {
LOG.assertTrue(getNotNulls().contains(defaultNotNull));
myDefaultNotNull = defaultNotNull;
}
public boolean isNullable(PsiModifierListOwner owner, boolean checkBases) {
return AnnotationUtil.isAnnotated(owner, getNullables(), checkBases);
}
public boolean isNotNull(PsiModifierListOwner owner, boolean checkBases) {
return AnnotationUtil.isAnnotated(owner, getNotNulls(), checkBases);
}
public List<String> getNullables() {
if (myNullables.isEmpty()) {
synchronized (LOCK) {
if (myNullables.isEmpty()) {
Collections.addAll(myNullables, DEFAULT_NULLABLES);
}
}
}
return myNullables;
}
public List<String> getNotNulls() {
if (myNotNulls.isEmpty()) {
synchronized (LOCK) {
if (myNotNulls.isEmpty()) {
Collections.addAll(myNotNulls, DEFAULT_NOT_NULLS);
}
}
}
return myNotNulls;
}
public boolean hasDefaultValues() {
if (DEFAULT_NULLABLES.length != getNullables().size() || DEFAULT_NOT_NULLS.length != getNotNulls().size()) {
return false;
}
if (myDefaultNotNull != AnnotationUtil.NOT_NULL || myDefaultNullable != AnnotationUtil.NULLABLE) {
return false;
}
for (int i = 0; i < DEFAULT_NULLABLES.length; i++) {
if (!getNullables().get(i).equals(DEFAULT_NULLABLES[i])) {
return false;
}
}
for (int i = 0; i < DEFAULT_NOT_NULLS.length; i++) {
if (!getNotNulls().get(i).equals(DEFAULT_NOT_NULLS[i])) {
return false;
}
}
return true;
}
@Override
public Element getState() {
final Element component = new Element("component");
if (hasDefaultValues()) {
return component;
}
try {
DefaultJDOMExternalizer.writeExternal(this, component);
}
catch (WriteExternalException e) {
LOG.error(e);
}
return component;
}
@Override
public void loadState(Element state) {
try {
DefaultJDOMExternalizer.readExternal(this, state);
}
catch (InvalidDataException e) {
LOG.error(e);
}
}
public static boolean isNullable(@NotNull PsiModifierListOwner owner) {
return !isNotNull(owner) && getInstance(owner.getProject()).isNullable(owner, true);
}
public static boolean isNotNull(@NotNull PsiModifierListOwner owner) {
return getInstance(owner.getProject()).isNotNull(owner, true);
}
}
@@ -0,0 +1,617 @@
/*
* Copyright 2000-2009 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.codeInsight.NullableNotNullManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.beans.Introspector;
import java.util.*;
/**
* @author Mike
*/
public class PropertyUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.PropertyUtil");
private PropertyUtil() {
}
public static boolean isSimplePropertyGetter(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();
PsiType returnType = method.getReturnType();
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;
}
if (returnType != null && PsiType.VOID.equals(returnType)) return false;
}
else if (methodName.startsWith("is") && methodNameLength > "is".length()) {
if (Character.isLowerCase(methodName.charAt("is".length()))
&& (methodNameLength == "is".length() + 1 || Character.isLowerCase(methodName.charAt("is".length() + 1)))) {
return false;
}
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());
}
@Nullable public static String getPropertyName(PsiMethod method) {
if (isSimplePropertyGetter(method)) {
return getPropertyNameByGetter(method);
}
else if (isSimplePropertySetter(method)) {
return getPropertyNameBySetter(method);
}
else {
return null;
}
}
@NotNull
public static String getPropertyNameByGetter(PsiMethod getterMethod) {
@NonNls String methodName = getterMethod.getName();
return methodName.startsWith("get") ?
StringUtil.decapitalize(methodName.substring(3)) :
StringUtil.decapitalize(methodName.substring(2));
}
@NotNull
public static String getPropertyNameBySetter(PsiMethod setterMethod) {
String methodName = setterMethod.getName();
return Introspector.decapitalize(methodName.substring(3));
}
@NotNull
public static Map<String, PsiMethod> getAllProperties(@NotNull final PsiClass psiClass, final boolean acceptSetters, final boolean acceptGetters) {
return getAllProperties(psiClass, acceptSetters, acceptGetters, true);
}
@NotNull
public static Map<String, PsiMethod> getAllProperties(@NotNull final PsiClass psiClass, final boolean acceptSetters, final boolean acceptGetters, final boolean includeSuperClass) {
final Map<String, PsiMethod> map = new HashMap<String, PsiMethod>();
final PsiMethod[] methods = includeSuperClass ? psiClass.getAllMethods() : psiClass.getMethods();
for (PsiMethod method : methods) {
if (filterMethods(method)) continue;
if (acceptSetters && isSimplePropertySetter(method)||
acceptGetters && isSimplePropertyGetter(method)) {
map.put(getPropertyName(method), method);
}
}
return map;
}
private static boolean filterMethods(final PsiMethod method) {
if(method.hasModifierProperty(PsiModifier.STATIC) || !method.hasModifierProperty(PsiModifier.PUBLIC)) return true;
PsiClass psiClass = method.getContainingClass();
if (psiClass == null) return false;
final String className = psiClass.getQualifiedName();
return className != null && className.equals(CommonClassNames.JAVA_LANG_OBJECT);
}
@NotNull
public static List<PsiMethod> getSetters(@NotNull final PsiClass psiClass, final String propertyName) {
final String setterName = suggestSetterName(propertyName);
final PsiMethod[] psiMethods = psiClass.findMethodsByName(setterName, true);
final ArrayList<PsiMethod> list = new ArrayList<PsiMethod>(psiMethods.length);
for (PsiMethod method : psiMethods) {
if (filterMethods(method)) continue;
if (isSimplePropertySetter(method)) {
list.add(method);
}
}
return list;
}
@NotNull
public static List<PsiMethod> getGetters(@NotNull final PsiClass psiClass, final String propertyName) {
final String[] names = suggestGetterNames(propertyName);
final ArrayList<PsiMethod> list = new ArrayList<PsiMethod>();
for (String name : names) {
final PsiMethod[] psiMethods = psiClass.findMethodsByName(name, true);
for (PsiMethod method : psiMethods) {
if (filterMethods(method)) continue;
if (isSimplePropertyGetter(method)) {
list.add(method);
}
}
}
return list;
}
@NotNull
public static List<PsiMethod> getAccessors(@NotNull final PsiClass psiClass, final String propertyName) {
return ContainerUtil.concat(getGetters(psiClass, propertyName), getSetters(psiClass, propertyName));
}
@Nullable
public static PsiMethod findPropertyGetter(PsiClass aClass,
String propertyName,
boolean isStatic,
boolean checkSuperClasses) {
if (aClass == null) return null;
PsiMethod[] methods;
if (checkSuperClasses) {
methods = aClass.getAllMethods();
}
else {
methods = aClass.getMethods();
}
for (PsiMethod method : methods) {
if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
if (isSimplePropertyGetter(method)) {
if (getPropertyNameByGetter(method).equals(propertyName)) {
return method;
}
}
}
return null;
}
@Nullable
public static PsiMethod findPropertyGetterWithType(String propertyName, boolean isStatic, PsiType type, Iterator<PsiMethod> methods) {
while (methods.hasNext()) {
PsiMethod method = methods.next();
if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
if (isSimplePropertyGetter(method)) {
if (getPropertyNameByGetter(method).equals(propertyName)) {
if (type.equals(method.getReturnType())) return method;
}
}
}
return null;
}
public static boolean isSimplePropertyAccessor(PsiMethod method) {
return isSimplePropertyGetter(method) || isSimplePropertySetter(method);
}
@Nullable public static PsiMethod findPropertySetter(PsiClass aClass,
String propertyName,
boolean isStatic,
boolean checkSuperClasses) {
if (aClass == null) return null;
PsiMethod[] methods;
if (checkSuperClasses) {
methods = aClass.getAllMethods();
}
else {
methods = aClass.getMethods();
}
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()) {
PsiMethod method = methods.next();
if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
if (isSimplePropertySetter(method)) {
if (getPropertyNameBySetter(method).equals(propertyName)) {
PsiType methodType = method.getParameterList().getParameters()[0].getType();
if (type.equals(methodType)) return method;
}
}
}
return null;
}
@Nullable public static PsiField findPropertyField(Project project, 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(project, field))) return field;
}
return null;
}
@Nullable public static PsiField findPropertyFieldWithType(Project project, String propertyName,
boolean isStatic, PsiType type, Iterator<PsiField> fields) {
while (fields.hasNext()) {
PsiField field = fields.next();
if (field.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
if (propertyName.equals(suggestPropertyName(project, field))) {
if (type.equals(field.getType())) return field;
}
}
return null;
}
@Nullable public static String getPropertyName(@NonNls String methodName) {
return StringUtil.getPropertyName(methodName);
}
public static String suggestGetterName(@NonNls @NotNull String propertyName, @Nullable PsiType propertyType) {
return suggestGetterName(propertyName, propertyType, null);
}
public static String suggestGetterName(@NotNull String propertyName, @Nullable PsiType propertyType, @NonNls String existingGetterName) {
@NonNls StringBuffer name = new StringBuffer(StringUtil.capitalizeWithJavaBeanConvention(propertyName));
if (isBoolean(propertyType)) {
if (existingGetterName == null || !existingGetterName.startsWith("get")) {
name.insert(0, "is");
}
else {
name.insert(0, "get");
}
}
else {
name.insert(0, "get");
}
return name.toString();
}
private static boolean isBoolean(@Nullable PsiType propertyType) {
return PsiType.BOOLEAN.equals(propertyType);
}
@NonNls
public static String[] suggestGetterNames(String propertyName) {
final String str = StringUtil.capitalizeWithJavaBeanConvention(propertyName);
return new String[] { "is" + str, "get" + str };
}
public static String suggestSetterName(@NonNls String propertyName) {
@NonNls StringBuffer name = new StringBuffer(StringUtil.capitalizeWithJavaBeanConvention(propertyName));
name.insert(0, "set");
return name.toString();
}
public static String[] getReadableProperties(PsiClass aClass, boolean includeSuperClass) {
List<String> result = new ArrayList<String>();
PsiMethod[] methods;
if (includeSuperClass) {
methods = aClass.getAllMethods();
}
else {
methods = 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);
}
public static String[] getWritableProperties(PsiClass aClass, boolean includeSuperClass) {
List<String> result = new ArrayList<String>();
PsiMethod[] methods;
if (includeSuperClass) {
methods = aClass.getAllMethods();
}
else {
methods = 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);
}
public static PsiMethod generateGetterPrototype(@NotNull PsiField field) {
PsiElementFactory factory = JavaPsiFacade.getInstance(field.getProject()).getElementFactory();
Project project = field.getProject();
String name = field.getName();
String getName = suggestGetterName(project, field);
try {
PsiMethod getMethod = factory.createMethod(getName, field.getType());
PsiUtil.setModifierProperty(getMethod, PsiModifier.PUBLIC, true);
if (field.hasModifierProperty(PsiModifier.STATIC)) {
PsiUtil.setModifierProperty(getMethod, PsiModifier.STATIC, true);
}
annotateWithNullableStuff(field, factory, getMethod);
PsiCodeBlock body = factory.createCodeBlockFromText("{\nreturn " + name + ";\n}", null);
getMethod.getBody().replace(body);
getMethod = (PsiMethod)CodeStyleManager.getInstance(project).reformat(getMethod);
return getMethod;
}
catch (IncorrectOperationException e) {
LOG.error(e);
return null;
}
}
public static PsiMethod generateSetterPrototype(PsiField field) {
return generateSetterPrototype(field, field.getContainingClass());
}
public static PsiMethod generateSetterPrototype(PsiField field, final PsiClass containingClass) {
return generateSetterPrototype(field, containingClass, false);
}
public static PsiMethod generateSetterPrototype(PsiField field, final PsiClass containingClass, boolean returnSelf) {
Project project = field.getProject();
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
PsiElementFactory factory = JavaPsiFacade.getInstance(field.getProject()).getElementFactory();
String name = field.getName();
boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC);
VariableKind kind = codeStyleManager.getVariableKind(field);
String propertyName = codeStyleManager.variableNameToPropertyName(name, kind);
String setName = suggestSetterName(project, field);
try {
PsiMethod setMethod = factory.createMethod(setName, returnSelf ? factory.createType(containingClass) : PsiType.VOID);
String parameterName = codeStyleManager.propertyNameToVariableName(propertyName, VariableKind.PARAMETER);
PsiParameter param = factory.createParameter(parameterName, field.getType());
annotateWithNullableStuff(field, factory, param);
setMethod.getParameterList().add(param);
PsiUtil.setModifierProperty(setMethod, PsiModifier.PUBLIC, true);
PsiUtil.setModifierProperty(setMethod, PsiModifier.STATIC, isStatic);
@NonNls StringBuffer buffer = new StringBuffer();
buffer.append("{\n");
if (name.equals(parameterName)) {
if (!isStatic) {
buffer.append("this.");
}
else {
String className = containingClass.getName();
if (className != null) {
buffer.append(className);
buffer.append(".");
}
}
}
buffer.append(name);
buffer.append("=");
buffer.append(parameterName);
buffer.append(";\n");
if (returnSelf) {
buffer.append("return this;\n");
}
buffer.append("}");
PsiCodeBlock body = factory.createCodeBlockFromText(buffer.toString(), null);
setMethod.getBody().replace(body);
setMethod = (PsiMethod)CodeStyleManager.getInstance(project).reformat(setMethod);
return setMethod;
}
catch (IncorrectOperationException e) {
LOG.error(e);
return null;
}
}
private static void annotateWithNullableStuff(final PsiModifierListOwner field, final PsiElementFactory factory, final PsiModifierListOwner listOwner)
throws IncorrectOperationException {
final NullableNotNullManager manager = NullableNotNullManager.getInstance(field.getProject());
final String notNull = manager.getNotNull(field);
if (notNull != null) {
annotate(factory, listOwner, notNull);
}
else {
final String nullable = manager.getNullable(field);
if (nullable != null) {
annotate(factory, listOwner, nullable);
}
}
}
private static void annotate(final PsiElementFactory factory, final PsiModifierListOwner listOwner, final String annotationQName)
throws IncorrectOperationException {
final PsiModifierList modifierList = listOwner.getModifierList();
LOG.assertTrue(modifierList != null);
modifierList.addAfter(factory.createAnnotationFromText("@" + annotationQName, listOwner), null);
}
public static String suggestPropertyName(Project project, PsiField field) {
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
VariableKind kind = codeStyleManager.getVariableKind(field);
return codeStyleManager.variableNameToPropertyName(field.getName(), kind);
}
public static String suggestGetterName(Project project, PsiField field) {
String propertyName = suggestPropertyName(project, field);
return suggestGetterName(propertyName, field.getType());
}
public static String suggestSetterName(Project project, PsiField field) {
String propertyName = suggestPropertyName(project, field);
return suggestSetterName(propertyName);
}
/**
* "xxx", "void setMyProperty(String pp)" -> "setXxx"
*/
@Nullable
public static String suggestPropertyAccessor(String name, PsiMethod accessorTemplate) {
if (isSimplePropertyGetter(accessorTemplate)) {
PsiType type = accessorTemplate.getReturnType();
return suggestGetterName(name, type, accessorTemplate.getName());
}
if (isSimplePropertySetter(accessorTemplate)) {
return suggestSetterName(name);
}
return null;
}
@Nullable
public static String getPropertyName(final PsiMember member) {
if (member instanceof PsiMethod) {
return getPropertyName((PsiMethod)member);
}
else if (member instanceof PsiField) {
return member.getName();
}
else return null;
}
@Nullable
public static PsiType getPropertyType(final PsiMember member) {
if (member instanceof PsiField) {
return ((PsiField)member).getType();
}
else 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) {
return ((PsiField)member).getTypeElement();
}
else if (member instanceof PsiMethod) {
final PsiMethod psiMethod = (PsiMethod)member;
if (isSimplePropertyGetter(psiMethod)) {
return psiMethod.getReturnTypeElement();
}
else if (isSimplePropertySetter(psiMethod)) {
return psiMethod.getParameterList().getParameters()[0].getTypeElement();
}
}
return null;
}
@Nullable
public static PsiIdentifier getPropertyNameIdentifier(final PsiMember member) {
if (member instanceof PsiField) {
return ((PsiField)member).getNameIdentifier();
}
else if (member instanceof PsiMethod) {
return ((PsiMethod)member).getNameIdentifier();
}
return null;
}
@Nullable
public static PsiField findPropertyFieldByMember(final PsiMember psiMember) {
if (psiMember instanceof PsiField) {
return (PsiField)psiMember;
}
else if (psiMember instanceof PsiMethod) {
final PsiMethod psiMethod = (PsiMethod)psiMember;
final PsiType returnType = psiMethod.getReturnType();
if (returnType == null) return null;
final PsiCodeBlock body = psiMethod.getBody();
final PsiStatement[] statements = body == null? null : body.getStatements();
final PsiStatement statement = statements == null || statements.length != 1? null : statements[0];
final PsiElement target;
if (PsiType.VOID.equals(returnType)) {
final PsiExpression expression =
statement instanceof PsiExpressionStatement ? ((PsiExpressionStatement)statement).getExpression() : null;
target = expression instanceof PsiAssignmentExpression ? ((PsiAssignmentExpression)expression).getLExpression() : null;
}
else {
target = statement instanceof PsiReturnStatement ? ((PsiReturnStatement)statement).getReturnValue() : null;
}
final PsiElement resolved = target instanceof PsiReferenceExpression ? ((PsiReferenceExpression)target).resolve() : null;
if (resolved instanceof PsiField) {
final PsiField field = (PsiField)resolved;
if (psiMember.getContainingClass() == field.getContainingClass() ||
psiMember.getContainingClass().isInheritor(field.getContainingClass(), true)) return field;
}
}
return null;
}
}