How Implement find usage for string literal method call
Hey,
I found various example of implementing find usage and the documentation doesn't help me much in this case, so I’m little confused here.
If I want to find usages for “MyBeanClass.myBeanMethod” and it should show all the places this method is called, even if it’s a string literal like I shown in the JavaCamelRouteBuilder class and “.bean(MyBeanClass.class, "myBeanMethod")”
What will be the best way to implement this?
public class MyBeanClass {
public void myBeanMethod() { } // ← call find usage her
}
public class JavaCamelRouteBuilder extends RouteBuilder {
public void configure() throws Exception {
from("file:inbox")
.bean(MyBeanClass.class, "myBeanMethod") // expect it show this call
.to("file:outbox");
}
}
Thanks,
/Flemming
Please sign in to leave a comment.
Do you have a reference resolving to the method in that string literal? You should create a PsiReferenceContributor for that.
I have already created a PsiReferenceContributor for the string literal to the PsiMethod and that's is working fine, but as far I understand you can't create reference from PsiMethod to the string literal?
You can't create a reference from PsiMethod to the literal, and you shouldn't.
Does Goto Declaration work from the literal to the method now? Does Find Usages find that reference now?
Yes it works fine from the literal to the method, but not the other way around and that's the my problem.
Is your reference's `isReferenceTo` method called during Find Usages?
The 'isReferenceTo' is called, but I did not override it my PsiReference code, is that required ?
public class CamelBeanMethodReference extends PsiReferenceBase<PsiClass> implements PsiPolyVariantReference {
private final PsiLiteral beanNameElement;
private final String methodName;
CamelBeanMethodReference(PsiClass element, PsiLiteral beanNameElement, String methodName, TextRange textRange) {
super(element, textRange);
this.beanNameElement = beanNameElement;
this.methodName = methodName;
}
@NotNull
@Override
public ResolveResult[] multiResolve(boolean b) {
List<ResolveResult> results = new ArrayList<>();
final PsiMethod[] methodsByName = getElement().findMethodsByName(methodName, true);
for (PsiMethod psiMethod : methodsByName) {
final boolean isPrivate = getCamelIdeaUtils().isOneOfModifierType(psiMethod, JvmModifier.PRIVATE, JvmModifier.ABSTRACT);
if (!isPrivate) {
if (getCamelIdeaUtils().isAnnotatedWithHandler(psiMethod)) {
return new ResolveResult[] {new PsiElementResolveResult(psiMethod)};
}
results.add(new PsiElementResolveResult(psiMethod));
}
}
return results.toArray(new ResolveResult[results.size()]);
}
@Nullable
@Override
public PsiElement resolve() {
ResolveResult[] resolveResults = multiResolve(false);
return resolveResults.length == 1 ? resolveResults[0].getElement() : null;
}
@NotNull
@Override
public Object[] getVariants() {
return new Object[0];
}
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
//Find all the method with the registered method name on it's class.
final PsiMethod[] methodsByName = getElement().findMethodsByName(methodName, true);
for (PsiMethod psiMethod : methodsByName) {
psiMethod.setName(newElementName);
}
//Rename the Camel DSL bean ref method
ElementManipulators.getManipulator(beanNameElement).handleContentChange(this.beanNameElement, this.getRangeInElement(), newElementName);
return getElement();
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
return super.bindToElement(element);
}
private CamelIdeaUtils getCamelIdeaUtils() {
return ServiceManager.getService(CamelIdeaUtils.class);
}
@Override
public boolean isSoft() {
return false;
}
}
The default implementation is usually good enough, but you might want to override it for performance reasons (to avoid expensive resolve when not needed) or if it doesn't work correctly. Does isReferenceTo return true in your case?
yes its' true and I my some observation when debugging this:
You might want to try extending PsiPolyVariantReferenceBase instead, then the issue with overloaded methods should be fixed.
If that still doesn't fix Find Usages, please post here a stack trace when isReferenceTo is called (and returns true) for your reference during usage search.
The PsiPolyVariantReferenceBase seems to fix the overloaded methods, but still not able to find the reference to "myBeanMethod"
Stacktrace :
The stack trace seems correct. I can only suggest to do some stepping in the debugger to see what happens after you return true. Normally, this leads to the reference being returned from the search as a result, but maybe there's something else intervening in your case.
I finally figure out what is going on here. It turned out I have been using the PsiPolyVariantReferenceBase wrong, and assign it with the element it should referer to and not with the element it referer from.
And the result when calling "find usage" on the method “MyBeanClass.myBeanMethod” the "isReferenceTo(PsiElement element)" referer to the method it self, and not the “.bean(MyBeanClass.class, "myBeanMethod")” as it should.
I follow your code, but not found pis-literal in class's field find-usage action.
public class ElBeanFieldReference extends ElReference {
private final String elExpressionFragment;
private final PsiClass psiClass;
public ElBeanFieldReference(@NotNull final PsiElement element, final TextRange range,
final String elExpressionFragment, final PsiClass psiClass) {
super(element, range);
this.elExpressionFragment = elExpressionFragment;
this.psiClass = psiClass;
}
@Override
public ResolveResult @NotNull [] multiResolve(final boolean incompleteCode) {
final PsiField psiField = psiClass.findFieldByName(elExpressionFragment, true);
if (psiField == null) {
return new ResolveResult[0];
}
return new ResolveResult[]{new PsiElementResolveResult(psiField)};
}
}
What's the problem with my code ?
Builder please create a new thread and clarify your problem, please link your full plugin sources if possible