MethodInvocationExpr.java
/*******************************************************************************
* Copyright (c) 2026 Carsten Hammer.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Carsten Hammer
*******************************************************************************/
package org.sandbox.ast.api.expr;
import java.util.List;
import java.util.Optional;
import org.sandbox.ast.api.info.MethodInfo;
import org.sandbox.ast.api.info.TypeInfo;
/**
* Immutable record representing a method invocation expression.
* Provides fluent access to receiver, arguments, and method information.
*
* <p>Example usage:</p>
* <pre>
* // Old style:
* if (node instanceof MethodInvocation) {
* MethodInvocation mi = (MethodInvocation) node;
* Expression receiver = mi.getExpression();
* if (receiver instanceof SimpleName) {
* SimpleName name = (SimpleName) receiver;
* // ...
* }
* }
*
* // New style:
* expr.asMethodInvocation()
* .flatMap(MethodInvocationExpr::receiver)
* .filter(ASTExpr::isSimpleName)
* .ifPresent(name -> { });
* </pre>
*/
public record MethodInvocationExpr(
Optional<ASTExpr> receiver,
List<ASTExpr> arguments,
Optional<MethodInfo> method,
Optional<TypeInfo> type
) implements ASTExpr {
/**
* Creates a MethodInvocationExpr record.
*
* @param receiver the receiver expression (empty for unqualified calls)
* @param arguments the method arguments
* @param method the resolved method information
* @param type the result type of the invocation
*/
public MethodInvocationExpr {
receiver = receiver == null ? Optional.empty() : receiver;
arguments = arguments == null ? List.of() : List.copyOf(arguments);
method = method == null ? Optional.empty() : method;
type = type == null ? Optional.empty() : type;
}
/**
* Checks if this invocation has a receiver expression.
*
* @return true if receiver is present
*/
public boolean hasReceiver() {
return receiver.isPresent();
}
/**
* Gets the number of arguments.
*
* @return argument count
*/
public int argumentCount() {
return arguments.size();
}
/**
* Checks if this is a call to a specific method.
*
* @param methodName the method name
* @param paramCount the expected parameter count
* @return true if method matches
*/
public boolean isMethodCall(String methodName, int paramCount) {
return method.map(m -> m.name().equals(methodName) &&
m.parameters().size() == paramCount)
.orElse(false);
}
/**
* Checks if this is a call to a method on a specific type.
*
* @param typeName the fully qualified type name
* @param methodName the method name
* @return true if method matches
*/
public boolean isMethodCall(String typeName, String methodName) {
return method.map(m -> m.name().equals(methodName) &&
m.declaringType() != null &&
m.declaringType().is(typeName))
.orElse(false);
}
/**
* Checks if this is a static method call.
*
* @return true if static
*/
public boolean isStatic() {
return method.map(MethodInfo::isStatic).orElse(false);
}
/**
* Checks if the receiver has a specific type.
*
* @param qualifiedTypeName the fully qualified type name
* @return true if receiver has this type
*/
public boolean receiverHasType(String qualifiedTypeName) {
return receiver.flatMap(ASTExpr::type)
.map(t -> t.is(qualifiedTypeName))
.orElse(false);
}
/**
* Gets an argument by index.
*
* @param index the argument index
* @return the argument, or empty if index out of bounds
*/
public Optional<ASTExpr> argument(int index) {
if (index < 0 || index >= arguments.size()) {
return Optional.empty();
}
return Optional.of(arguments.get(index));
}
/**
* Checks if this is a chained method call (receiver is also a method invocation).
*
* @return true if chained
*/
public boolean isChained() {
return receiver.map(ASTExpr::isMethodInvocation).orElse(false);
}
/**
* Gets the method name if available.
*
* @return the method name, or empty if not resolved
*/
public Optional<String> methodName() {
return method.map(MethodInfo::name);
}
/**
* Checks if this method has the given name.
*
* @param name the method name
* @return true if method has this name
*/
public boolean isMethodNamed(String name) {
return method.map(m -> m.name().equals(name)).orElse(false);
}
/**
* Gets the receiver's identifier if receiver is a SimpleName.
*
* @return the receiver identifier, or empty if not a simple name
*/
public Optional<String> receiverIdentifier() {
return receiver.flatMap(ASTExpr::asSimpleName)
.map(SimpleNameExpr::identifier);
}
/**
* Checks if this method's return type matches the given qualified name.
*
* @param qualifiedName the fully qualified type name
* @return true if return type matches
*/
public boolean returnsType(String qualifiedName) {
return method.map(m -> m.returnType().is(qualifiedName)).orElse(false);
}
/**
* Builder for creating MethodInvocationExpr instances.
*/
public static class Builder {
private Optional<ASTExpr> receiver = Optional.empty();
private java.util.ArrayList<ASTExpr> arguments = new java.util.ArrayList<>();
private Optional<MethodInfo> method = Optional.empty();
private Optional<TypeInfo> type = Optional.empty();
/**
* Sets the receiver expression.
*
* @param receiver the receiver
* @return this builder
*/
public Builder receiver(ASTExpr receiver) {
this.receiver = Optional.ofNullable(receiver);
return this;
}
/**
* Sets the arguments.
*
* @param arguments the arguments
* @return this builder
*/
public Builder arguments(List<ASTExpr> arguments) {
this.arguments = new java.util.ArrayList<>(arguments == null ? List.of() : arguments);
return this;
}
/**
* Adds a single argument.
*
* @param argument the argument
* @return this builder
*/
public Builder addArgument(ASTExpr argument) {
this.arguments.add(argument);
return this;
}
/**
* Sets the method information.
*
* @param method the method
* @return this builder
*/
public Builder method(MethodInfo method) {
this.method = Optional.ofNullable(method);
return this;
}
/**
* Sets the type information.
*
* @param type the type
* @return this builder
*/
public Builder type(TypeInfo type) {
this.type = Optional.ofNullable(type);
return this;
}
/**
* Builds the MethodInvocationExpr.
*
* @return the method invocation
*/
public MethodInvocationExpr build() {
return new MethodInvocationExpr(receiver, arguments, method, type);
}
}
/**
* Creates a new builder.
*
* @return a new builder
*/
public static Builder builder() {
return new Builder();
}
}