下面列出了怎么用com.intellij.psi.PsiMethodCallExpression的API类实例代码及写法,或者点击链接到github查看源代码。
/**
* If element is a {@link PsiStatement} with {@code Component.create()} call, finds missing
* required props for it.
*
* @param element element to verify
* @param errorHandler handles a list of missing required props and reference to the {@code
* Component.create()} call in the statement
* @param generatedClassResolver returns generated Litho class, or null if the provided method
* doesn't belong to any
*/
static void annotate(
PsiElement element,
BiConsumer<Collection<String>, PsiReferenceExpression> errorHandler,
Function<PsiMethodCallExpression, PsiClass> generatedClassResolver) {
if (element instanceof PsiDeclarationStatement) {
Arrays.stream(((PsiDeclarationStatement) element).getDeclaredElements())
.filter(PsiVariable.class::isInstance)
.map(declaredVariable -> ((PsiVariable) declaredVariable).getInitializer())
.forEach(
expression -> handleIfMethodCall(expression, errorHandler, generatedClassResolver));
} else if (element instanceof PsiExpressionStatement) {
handleIfMethodCall(
((PsiExpressionStatement) element).getExpression(), errorHandler, generatedClassResolver);
} else if (element instanceof PsiReturnStatement) {
handleIfMethodCall(
((PsiReturnStatement) element).getReturnValue(), errorHandler, generatedClassResolver);
}
}
@Test
public void markStatement() {
testHelper.getPsiClass(
psiClasses -> {
assertThat(psiClasses).hasSize(2);
PsiClass underTest = psiClasses.get(0);
PsiClass component = psiClasses.get(1);
// For testing environment
Function<PsiMethodCallExpression, PsiClass> resolver = ignored -> component;
RequiredPropLineMarkerProvider provider = new RequiredPropLineMarkerProvider(resolver);
List<PsiElement> statements =
new ArrayList<>(PsiTreeUtil.findChildrenOfAnyType(underTest, PsiStatement.class));
assertThat(provider.getLineMarkerInfo(statements.get(0))).isNotNull();
assertThat(provider.getLineMarkerInfo(statements.get(1))).isNull();
return true;
},
"RequiredPropAnnotatorTest.java",
"RequiredPropAnnotatorComponent.java");
}
@Test
public void createAddMethodCallFix() {
testHelper.getPsiClass(
classes -> {
// Setup test environment
PsiClass cls = classes.get(0);
PsiMethodCallExpression call =
PsiTreeUtil.findChildOfType(cls, PsiMethodCallExpression.class);
Project project = testHelper.getFixture().getProject();
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
Editor editor = mock(Editor.class);
when(editor.getCaretModel()).thenReturn(mock(CaretModel.class));
IntentionAction fix =
AddArgumentFix.createAddMethodCallFix(call, "ClassName", "methodName", factory);
assertThat(call.getArgumentList().getExpressions()[0].getText())
.isNotEqualTo("ClassName.methodName()");
fix.invoke(project, editor, testHelper.getFixture().getFile());
assertThat(call.getArgumentList().getExpressions()[0].getText())
.isEqualTo("ClassName.methodName()");
return true;
},
"RequiredPropAnnotatorTest.java");
}
/** Creates new fix, that adds static method call as an argument to the originalMethodCall. */
static IntentionAction createAddMethodCallFix(
PsiMethodCallExpression originalMethodCall,
String clsName,
String methodName,
PsiElementFactory elementFactory) {
PsiExpressionList newArgumentList =
createArgumentList(originalMethodCall.getContext(), clsName, methodName, elementFactory);
String fixDescription =
"Add ." + methodName + "() " + getCapitalizedMethoName(originalMethodCall);
return new AddArgumentFix(originalMethodCall, newArgumentList, fixDescription);
}
/**
* Creates new fix, that generates OnEvent method and adds static method call as an argument to
* the originalMethodCall.
*/
static IntentionAction createNewMethodCallFix(
PsiMethodCallExpression originalMethodCall,
String clsName,
PsiClass event,
PsiClass parentLayoutSpec) {
String fixDescription = "Create new " + getCapitalizedMethoName(originalMethodCall);
return new OnEventCreateFix(
originalMethodCall, clsName, event, parentLayoutSpec, fixDescription);
}
static PsiExpressionList createArgumentList(
PsiElement context, String clsName, String methodName, PsiElementFactory elementFactory) {
final PsiMethodCallExpression stub =
(PsiMethodCallExpression)
elementFactory.createExpressionFromText(
"methodName(" + clsName + "." + methodName + "())", context);
return stub.getArgumentList();
}
/**
* Tries to guess if the given methodCall requires event handler.
*
* @return Qualified name of the handled Event or null, if methodCall neither accepts event
* handler, nor require fix.
*/
@Nullable
private static String resolveEventName(PsiMethodCallExpression methodCall) {
return Optional.of(methodCall.getMethodExpression().multiResolve(true))
.map(results -> results.length == 1 ? results[0] : JavaResolveResult.EMPTY)
.filter(MethodCandidateInfo.class::isInstance)
.map(MethodCandidateInfo.class::cast)
.filter(MethodCandidateInfo::isTypeArgumentsApplicable)
.filter(info -> !info.isApplicable() && !info.isValidResult())
.map(info -> info.getElement().getParameterList().getParameters())
.filter(parameters -> parameters.length > 0) // method(EventHandler<T> e)
.map(parameters -> parameters[0].getType())
.filter(PsiClassType.class::isInstance)
.filter(
parameterType -> {
String fullName = parameterType.getCanonicalText();
int genericIndex = fullName.indexOf('<');
if (genericIndex <= 0) {
return false;
}
String className = fullName.substring(0, genericIndex);
return LithoClassNames.EVENT_HANDLER_CLASS_NAME.equals(className);
})
.map(parameterType -> ((PsiClassType) parameterType).getParameters())
.filter(generics -> generics.length == 1) // <T>
.map(generics -> generics[0].getCanonicalText())
.orElse(null);
}
private static void handleIfMethodCall(
@Nullable PsiExpression expression,
BiConsumer<Collection<String>, PsiReferenceExpression> errorHandler,
Function<PsiMethodCallExpression, PsiClass> generatedClassResolver) {
if (expression instanceof PsiMethodCallExpression) {
PsiMethodCallExpression rootMethodCall = (PsiMethodCallExpression) expression;
handleMethodCall(rootMethodCall, new HashSet<>(), errorHandler, generatedClassResolver);
}
}
private static void handleMethodCall(
PsiMethodCallExpression currentMethodCall,
Set<String> methodNamesCalled,
BiConsumer<Collection<String>, PsiReferenceExpression> errorHandler,
Function<PsiMethodCallExpression, PsiClass> generatedClassResolver) {
PsiReferenceExpression methodExpression = currentMethodCall.getMethodExpression();
methodNamesCalled.add(methodExpression.getReferenceName());
// Assumption to find next method in a call chain
PsiMethodCallExpression nextMethodCall =
PsiTreeUtil.getChildOfType(methodExpression, PsiMethodCallExpression.class);
if (nextMethodCall != null) {
handleMethodCall(nextMethodCall, methodNamesCalled, errorHandler, generatedClassResolver);
} else if ("create".equals(methodExpression.getReferenceName())) {
// Finish call chain
// TODO T47712852: allow setting required prop in another statement
Optional.ofNullable(generatedClassResolver.apply(currentMethodCall))
.map(generatedCls -> collectMissingRequiredProps(generatedCls, methodNamesCalled))
.filter(result -> !result.isEmpty())
.ifPresent(
missingRequiredProps -> errorHandler.accept(missingRequiredProps, methodExpression));
}
PsiExpressionList argumentList = currentMethodCall.getArgumentList();
for (PsiExpression argument : argumentList.getExpressions()) {
handleIfMethodCall(argument, errorHandler, generatedClassResolver);
}
}
@Test
public void annotateStatement() {
testHelper.getPsiClass(
psiClasses -> {
assertEquals(2, psiClasses.size());
PsiClass underTest = psiClasses.get(0);
PsiClass component = psiClasses.get(1);
// For testing environment
Function<PsiMethodCallExpression, PsiClass> resolver =
psiMethodCallExpression -> component;
RequiredPropAnnotator annotator = new RequiredPropAnnotator(resolver);
TestHolder holder = new TestHolder();
Collection<PsiStatement> statements =
PsiTreeUtil.findChildrenOfAnyType(underTest, PsiStatement.class);
// Simulates IDE behavior of traversing Psi elements
for (PsiStatement statement : statements) {
annotator.annotate(statement, holder);
}
assertEquals(3, holder.errorMessages.size());
for (String errorMessage : holder.errorMessages) {
assertEquals(
"The following props are not "
+ "marked as optional and were not supplied: testRequiredPropName",
errorMessage);
}
assertEquals(3, holder.errorElements.size());
for (PsiElement errorElement : holder.errorElements) {
assertEquals("RequiredPropAnnotatorComponent.create", errorElement.getText());
}
return true;
},
"RequiredPropAnnotatorTest.java",
"RequiredPropAnnotatorComponent.java");
}
@Test
public void invoke() {
testHelper.getPsiClass(
classes -> {
final CodeInsightTestFixture fixture = testHelper.getFixture();
final PsiMethodCallExpression call =
PsiTreeUtil.findChildOfType(classes.get(0), PsiMethodCallExpression.class);
final LightVirtualFile virtualFile = createVirtualFile(classes.get(0));
final String generatedOnEvent =
"@com.facebook.litho.annotations.OnEvent(TestClass.class)\n"
+ " static void onTestClass(com.facebook.litho.ComponentContext c) {\n"
+ " }";
final PsiClass eventClass =
JavaPsiFacade.getInstance(fixture.getProject())
.getElementFactory()
.createClass("TestClass");
fixture.openFileInEditor(virtualFile);
assertThat(fixture.getEditor().getDocument().getText()).doesNotContain(generatedOnEvent);
AddArgumentFix.createNewMethodCallFix(
call, "TestName", eventClass, Mockito.mock(PsiClass.class))
.invoke(fixture.getProject(), fixture.getEditor(), fixture.getFile());
assertThat(fixture.getEditor().getDocument().getText()).contains(generatedOnEvent);
assertThat(call.getArgumentList().getExpressions()[0].getText())
.isEqualTo("TestName.onTestClass()");
return true;
},
"EventHandlerAnnotatorSpec.java");
}
@Override
public JavaElementVisitor createPsiVisitor(@NonNull final JavaContext context) {
return new JavaElementVisitor() {
@Override
public void visitLambdaExpression(PsiLambdaExpression expression) {
if (!(expression.getParent() instanceof PsiExpressionList)) {
return;
}
PsiExpressionList exprList = (PsiExpressionList) expression.getParent();
if (!(exprList.getParent() instanceof PsiMethodCallExpression)) {
return;
}
PsiMethodCallExpression call = (PsiMethodCallExpression) exprList.getParent();
if (call.getType() == null) {
return;
}
String callType = call.getType().getCanonicalText();
if (!callType.startsWith("de.mobilej.thinr.Thinr")) {
return;
}
markLeakSuspects(expression, expression, context);
}
};
}
@Override
public boolean isInsideCamelRoute(PsiElement element, boolean excludeRouteStart) {
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
if (call == null) {
return false;
}
if (!excludeRouteStart && getIdeaUtils().isFromJavaMethod(call, true, ROUTE_START)) {
return true;
}
Collection<PsiMethodCallExpression> chainedCalls = PsiTreeUtil.findChildrenOfType(call, PsiMethodCallExpression.class);
return chainedCalls.stream().anyMatch(c -> getIdeaUtils().isFromJavaMethod(c, true, ROUTE_START));
}
@Override
public boolean isElementFromSetterProperty(@NotNull PsiElement element, @NotNull String setter) {
// java method call
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
if (call != null) {
PsiMethod resolved = call.resolveMethod();
if (resolved != null) {
String javaSetter = "set" + Character.toUpperCase(setter.charAt(0)) + setter.substring(1);
return javaSetter.equals(resolved.getName());
}
}
return false;
}
/**
* Is the given element from a Java method call with any of the given method names
*
* @param element the psi element
* @param methods method call names
* @return <tt>true</tt> if matched, <tt>false</tt> otherwise
*/
public boolean isFromJavaMethodCall(PsiElement element, boolean fromRouteBuilder, String... methods) {
// java method call
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
if (call != null) {
return isFromJavaMethod(call, fromRouteBuilder, methods);
}
return false;
}
private boolean isRouteStartIdentifier(PsiIdentifier identifier, PsiElement resolvedIdentifier) {
// Eval methods from parent PsiMethodCallExpression to exclude start route method (from)
PsiElement element = identifier;
if (resolvedIdentifier instanceof PsiMethod) {
element = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
}
if (element == null) {
return false;
}
return getCamelIdeaUtils().isCamelRouteStartExpression(element);
}
/**
* Further refine search in order to match the exact Java Camel route.
* Checks if the given {@link PsiElement} contains a 'to' method that points to the give route.
*
* @param route the complete Camel route to search for
* @param psiElement the {@link PsiElement} that might contain the complete route definition
* @return the {@link PsiElement} that contains the exact match of the Camel route, null if there is no exact match
*/
private PsiElement findJavaElement(String route, PsiElement psiElement) {
Object value = psiElement.getText().replace("\"", "");
if (route.equals(value)) {
//the method 'to' is a PsiIdentifier not a PsiMethodCallExpression because it's part of method invocation chain
PsiMethodCallExpression methodCall = PsiTreeUtil.getParentOfType(psiElement, PsiMethodCallExpression.class);
if (methodCall != null) {
if (Arrays.stream(JAVA_ROUTE_CALL).anyMatch(s -> s.equals(methodCall.getMethodExpression().getReferenceName()))) {
return psiElement;
}
}
}
return null;
}
/**
* For the given gutters return all the gutter navigation targets that are {@link PsiMethodCallExpressionImpl} elements.
*/
static List<PsiMethodCallExpression> getGuttersWithJavaTarget(List<GotoRelatedItem> gutterList) {
return gutterList
.stream()
.filter(gotoRelatedItem -> gotoRelatedItem.getElement() instanceof PsiJavaToken)
.map(gotoRelatedItem -> PsiTreeUtil.getParentOfType(gotoRelatedItem.getElement(), PsiMethodCallExpression.class))
.collect(Collectors.toList());
}
public void testJavaDirectEndpointReference() {
myFixture.configureByText("RouteWithReferences.java", JAVA_ROUTE_WITH_REFERENCE);
PsiElement element = TestReferenceUtil.getParentElementAtCaret(myFixture);
List<PsiMethodCallExpression> results = TestReferenceUtil.resolveReference(element, PsiMethodCallExpression.class);
assertEquals(1, results.size());
assertEquals("from(\"direct:test\")", results.get(0).getText());
}
private void visitExpression(final PsiElement element) {
if (element.getNode().getElementType().equals(JavaElementType.NEW_EXPRESSION)) {
visitPsiNewExpression((PsiNewExpression) element);
} else if (element.getNode().getElementType().
equals(JavaElementType.METHOD_CALL_EXPRESSION)) {
visitPsiMethodCallExpression((PsiMethodCallExpression) element);
} else if (element.getNode().getElementType().
equals(JavaElementType.ASSIGNMENT_EXPRESSION)) {
visitPsiAssignmentExpression((PsiAssignmentExpression) element);
} else if (element.getNode().getElementType().
equals(JavaElementType.REFERENCE_EXPRESSION)) {
visitPsiReferenceExpression((PsiReferenceExpression) element);
}
}
private PsiMethodCallExpression createCall(@NonNls final String body) {
final PsiFile file = createFile("test.java", "class Test { " + body +
"void throwsMyException() throws MyException {}" +
"void throwsSomeException() throws SomeException {}" +
"static class MyException extends Exception {}" +
"static class SomeException extends Exception {}" +
"static class Exception {}" +
"}");
PsiMethodCallExpression methodCall = findMethodCall(file);
assertNotNull(methodCall);
return methodCall;
}
private static PsiMethodCallExpression findMethodCall(PsiElement element) {
if (element instanceof PsiMethodCallExpression) {
return (PsiMethodCallExpression) element;
}
for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) {
final PsiMethodCallExpression call = findMethodCall(child);
if (call != null) {
return call;
}
}
return null;
}
static PsiMethodCallExpression findMethodCall(PsiElement element) {
if (element == null) {
return null;
}
else if (element instanceof PsiMethodCallExpression) {
return (PsiMethodCallExpression) element;
} else {
return findMethodCall(element.getParent());
}
}
@Override public boolean shouldShow(Usage usage) {
PsiElement element = ((UsageInfo2UsageAdapter) usage).getElement();
PsiMethodCallExpression methodCall = PsiConsultantImpl.findMethodCall(element);
if (methodCall != null) {
PsiType[] expressionTypes = methodCall.getArgumentList().getExpressionTypes();
for (PsiType expressionType : expressionTypes) {
PsiClass argumentEventClass = PsiConsultantImpl.getClass(expressionType);
if (argumentEventClass.equals(this.eventClass)) {
return true;
}
}
}
return false;
}
public void setMethodInvocation(PsiMethodCallExpression methodInvocation) {
this.methodInvocation = toPointer(methodInvocation);
}
public PsiMethodCallExpression getMethodInvocation() {
return (PsiMethodCallExpression) this.methodInvocation.getElement();
}
public boolean instanceOf(PsiExpression expression) {
return expression instanceof PsiMethodCallExpression;
}
public Map<PsiMethodCallExpression, PsiMethod> getAdditionalMethodsToBeMoved() {
return additionalMethodsToBeMoved;
}
private List<MoveMethodCandidateRefactoring> identifyConceptualBindings(MyMethod method, Set<String> targetClasses) {
SmartList<MoveMethodCandidateRefactoring> candidateRefactoringList = new SmartList<>();
MethodObject methodObject = method.getMethodObject();
String sourceClass = method.getClassOrigin();
for (String targetClass : targetClasses) {
if (!targetClass.equals(sourceClass)) {
ClassObject targetClassObject = system.getClass(targetClass).getClassObject();
ListIterator<ParameterObject> parameterIterator = methodObject.getParameterListIterator();
while (parameterIterator.hasNext()) {
ParameterObject parameter = parameterIterator.next();
Association association = system.containsAssociationWithMultiplicityBetweenClasses(targetClass, parameter.getType().getClassType());
if (association != null) {
List<MethodInvocationObject> methodInvocations = methodObject.getMethodInvocations();
for (MethodInvocationObject methodInvocation : methodInvocations) {
if (methodInvocation.getOriginClassName().equals(targetClass)) {
PsiMethodCallExpression invocation = methodInvocation.getMethodInvocation();
boolean parameterIsPassedAsArgument = false;
PsiExpression[] invocationArguments = invocation.getArgumentList().getExpressions();
for (PsiExpression expression : invocationArguments) {
if (expression instanceof PsiReferenceExpression) {
PsiReferenceExpression argumentName = (PsiReferenceExpression) expression;
if (parameter.getSingleVariableDeclaration().equals(argumentName.resolve()))
parameterIsPassedAsArgument = true;
}
}
if (parameterIsPassedAsArgument) {
MethodObject invokedMethod = targetClassObject.getMethod(methodInvocation);
if (invokedMethod != null) {
List<FieldInstructionObject> fieldInstructions = invokedMethod.getFieldInstructions();
boolean containerFieldIsAccessed = false;
for (FieldInstructionObject fieldInstruction : fieldInstructions) {
if (association.getFieldObject().equals(fieldInstruction)) {
containerFieldIsAccessed = true;
break;
}
}
if (containerFieldIsAccessed) {
MyClass mySourceClass = classList.get(classIndexMap.get(sourceClass));
MyClass myTargetClass = classList.get(classIndexMap.get(targetClass));
MoveMethodCandidateRefactoring candidate = new MoveMethodCandidateRefactoring(system, mySourceClass, myTargetClass, method);
Map<PsiMethodCallExpression, PsiMethod> additionalMethodsToBeMoved = candidate.getAdditionalMethodsToBeMoved();
Collection<PsiMethod> values = additionalMethodsToBeMoved.values();
Set<String> methodEntitySet = entityMap.get(method.toString());
Set<String> sourceClassEntitySet = classMap.get(sourceClass);
Set<String> targetClassEntitySet = classMap.get(targetClass);
Set<String> intersectionWithSourceClass = DistanceCalculator.intersection(methodEntitySet, sourceClassEntitySet);
Set<String> intersectionWithTargetClass = DistanceCalculator.intersection(methodEntitySet, targetClassEntitySet);
Set<String> entitiesToRemoveFromIntersectionWithSourceClass = new LinkedHashSet<>();
if (!values.isEmpty()) {
for (String s : intersectionWithSourceClass) {
int entityPosition = entityIndexMap.get(s);
Entity e = entityList.get(entityPosition);
if (e instanceof MyMethod) {
MyMethod myInvokedMethod = (MyMethod) e;
if (values.contains(myInvokedMethod.getMethodObject().getMethodDeclaration())) {
entitiesToRemoveFromIntersectionWithSourceClass.add(s);
}
}
}
intersectionWithSourceClass.removeAll(entitiesToRemoveFromIntersectionWithSourceClass);
}
if (intersectionWithTargetClass.size() >= intersectionWithSourceClass.size()) {
if (candidate.isApplicable()) {
int sourceClassDependencies = candidate.getDistinctSourceDependencies();
int targetClassDependencies = candidate.getDistinctTargetDependencies();
if (sourceClassDependencies <= maximumNumberOfSourceClassMembersAccessedByMoveMethodCandidate
&& sourceClassDependencies < targetClassDependencies) {
candidateRefactoringList.add(candidate);
}
}
}
}
}
}
}
}
}
}
}
}
return candidateRefactoringList;
}
static String getCapitalizedMethoName(PsiMethodCallExpression methodCall) {
return StringUtil.capitalize(methodCall.getMethodExpression().getReferenceName());
}