MethodInfo.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.info;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Immutable record representing a method.
 * Provides fluent query methods for method checking and comparisons.
 */
public record MethodInfo(
	String name,
	TypeInfo declaringType,
	TypeInfo returnType,
	List<ParameterInfo> parameters,
	Set<Modifier> modifiers
) {
	
	/**
	 * Creates a MethodInfo record.
	 * 
	 * @param name method name
	 * @param declaringType type that declares this method
	 * @param returnType return type
	 * @param parameters list of parameters
	 * @param modifiers set of modifiers
	 */
	public MethodInfo {
		if (name == null) {
			throw new IllegalArgumentException("Method name cannot be null");
		}
		if (name.isEmpty()) {
			throw new IllegalArgumentException("Method name cannot be empty");
		}
		if (returnType == null) {
			throw new IllegalArgumentException("Return type cannot be null");
		}
		parameters = parameters == null ? List.of() : List.copyOf(parameters);
		modifiers = modifiers == null ? Set.of() : Set.copyOf(modifiers);
	}
	
	/**
	 * Checks if this is Math.max().
	 * 
	 * @return true if Math.max
	 */
	public boolean isMathMax() {
		return name.equals("max") && 
			   declaringType != null && 
			   declaringType.is("java.lang.Math") &&
			   parameters.size() == 2;
	}
	
	/**
	 * Checks if this is Math.min().
	 * 
	 * @return true if Math.min
	 */
	public boolean isMathMin() {
		return name.equals("min") && 
			   declaringType != null && 
			   declaringType.is("java.lang.Math") &&
			   parameters.size() == 2;
	}
	
	/**
	 * Checks if this is List.add().
	 * 
	 * @return true if List.add
	 */
	public boolean isListAdd() {
		return name.equals("add") && 
			   declaringType != null && 
			   declaringType.isList() &&
			   parameters.size() == 1;
	}
	
	/**
	 * Checks if this is List.get().
	 * 
	 * @return true if List.get
	 */
	public boolean isListGet() {
		return name.equals("get") && 
			   declaringType != null && 
			   declaringType.isList() &&
			   parameters.size() == 1;
	}
	
	/**
	 * Checks if this is Collection.stream().
	 * 
	 * @return true if Collection.stream
	 */
	public boolean isCollectionStream() {
		return name.equals("stream") && 
			   declaringType != null && 
			   declaringType.isCollection() &&
			   parameters.isEmpty();
	}
	
	/**
	 * Checks if this method has the given signature (name + parameter types).
	 * 
	 * @param methodName method name
	 * @param parameterTypes parameter type names (qualified)
	 * @return true if signature matches
	 */
	public boolean hasSignature(String methodName, String... parameterTypes) {
		if (!name.equals(methodName)) {
			return false;
		}
		if (parameters.size() != parameterTypes.length) {
			return false;
		}
		for (int i = 0; i < parameterTypes.length; i++) {
			if (!parameters.get(i).type().is(parameterTypes[i])) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Checks if this method has the specified modifier.
	 * 
	 * @param modifier modifier to check
	 * @return true if modifier is present
	 */
	public boolean hasModifier(Modifier modifier) {
		return modifiers.contains(modifier);
	}
	
	/**
	 * Checks if this method is static.
	 * 
	 * @return true if static
	 */
	public boolean isStatic() {
		return hasModifier(Modifier.STATIC);
	}
	
	/**
	 * Checks if this method is public.
	 * 
	 * @return true if public
	 */
	public boolean isPublic() {
		return hasModifier(Modifier.PUBLIC);
	}
	
	/**
	 * Checks if this method is private.
	 * 
	 * @return true if private
	 */
	public boolean isPrivate() {
		return hasModifier(Modifier.PRIVATE);
	}
	
	/**
	 * Checks if this method is abstract.
	 * 
	 * @return true if abstract
	 */
	public boolean isAbstract() {
		return hasModifier(Modifier.ABSTRACT);
	}
	
	/**
	 * Checks if this method is final.
	 * 
	 * @return true if final
	 */
	public boolean isFinal() {
		return hasModifier(Modifier.FINAL);
	}
	
	/**
	 * Returns the signature as a string (for debugging).
	 * 
	 * @return method signature
	 */
	public String signature() {
		String params = parameters.stream()
			.map(p -> p.type().simpleName())
			.collect(Collectors.joining(", "));
		return String.format("%s(%s)", name, params);
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj) return true;
		if (!(obj instanceof MethodInfo other)) return false;
		return Objects.equals(name, other.name) &&
			   Objects.equals(declaringType, other.declaringType) &&
			   Objects.equals(returnType, other.returnType) &&
			   Objects.equals(parameters, other.parameters);
	}
	
	@Override
	public int hashCode() {
		return Objects.hash(name, declaringType, returnType, parameters);
	}
	
	/**
	 * Builder for creating MethodInfo instances.
	 */
	public static class Builder {
		private String name;
		private TypeInfo declaringType;
		private TypeInfo returnType;
		private List<ParameterInfo> parameters = new java.util.ArrayList<>();
		private Set<Modifier> modifiers = Set.of();
		
		/**
		 * Creates a builder with the given method name.
		 * 
		 * @param name method name
		 * @return new Builder
		 */
		public static Builder named(String name) {
			Builder builder = new Builder();
			builder.name = name;
			return builder;
		}
		
		/**
		 * Sets the declaring type.
		 * 
		 * @param declaringType declaring type
		 * @return this builder
		 */
		public Builder declaringType(TypeInfo declaringType) {
			this.declaringType = declaringType;
			return this;
		}
		
		/**
		 * Sets the return type.
		 * 
		 * @param returnType return type
		 * @return this builder
		 */
		public Builder returnType(TypeInfo returnType) {
			this.returnType = returnType;
			return this;
		}
		
		/**
		 * Sets the parameters.
		 * 
		 * @param parameters list of parameters
		 * @return this builder
		 */
		public Builder parameters(List<ParameterInfo> parameters) {
			this.parameters = parameters;
			return this;
		}
		
		/**
		 * Adds a parameter.
		 * 
		 * @param parameter parameter to add
		 * @return this builder
		 */
		public Builder addParameter(ParameterInfo parameter) {
			parameters.add(parameter);
			return this;
		}
		
		/**
		 * Sets the modifiers.
		 * 
		 * @param modifiers set of modifiers
		 * @return this builder
		 */
		public Builder modifiers(Set<Modifier> modifiers) {
			this.modifiers = modifiers;
			return this;
		}
		
		/**
		 * Builds the MethodInfo instance.
		 * 
		 * @return new MethodInfo
		 */
		public MethodInfo build() {
			return new MethodInfo(name, declaringType, returnType, parameters, modifiers);
		}
	}
}