ExecutableBase.java

/*
 * Copyright © 2025 Indiana University
 * All rights reserved.
 *
 * BSD 3-Clause License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * - Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * 
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * 
 * - Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package iu.type;

import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import edu.iu.type.IuExecutable;
import edu.iu.type.IuExecutableKey;
import edu.iu.type.IuNamedElement;

/**
 * Facade implementation for an {@link Executable}.
 * 
 * @param <D> declaring type
 * @param <R> result type: constructor declaring type or method return type
 * @param <E> executable type: {@link Method} or {@link Constructor}
 */
abstract sealed class ExecutableBase<D, R, E extends Executable> extends DeclaredElementBase<D, E>
		implements IuExecutable<D, R>, ParameterizedFacade permits ConstructorFacade, MethodFacade {

	private final ParameterizedElement parameterizedElement = new ParameterizedElement();
	private final IuExecutableKey key;
	private final List<ParameterFacade<?>> parameters;

	/**
	 * Facade constructor.
	 * 
	 * @param executable            method or constructor
	 * @param type                  generic type associated with the element
	 * @param declaringTypeTemplate fully realized {@link TypeTemplate} for a
	 *                              generic type whose erasure declared the
	 *                              executable
	 */
	ExecutableBase(E executable, Type type, TypeTemplate<?, D> declaringTypeTemplate) {
		super(executable, type, declaringTypeTemplate);

		final String name;
		if (executable instanceof Method)
			name = ((Method) executable).getName();
		else
			name = null;
		this.key = IuExecutableKey.of(name, executable.getParameterTypes());

		List<ParameterFacade<?>> parameters = new ArrayList<>();
		this.parameters = Collections.unmodifiableList(parameters);

		final var allParameters = executable.getParameters();
		final var parameterCount = allParameters.length;

		final var genericParameterTypes = executable.getGenericParameterTypes();
		final var genericParameterCount = genericParameterTypes.length;
		final var nonGenericParameterCount = parameterCount - genericParameterCount;

		for (var parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++) {
			final var parameter = allParameters[parameterIndex];
			final var genericParameterIndex = parameterIndex - nonGenericParameterCount;

			TypeTemplate<?, ?> paramTypeTemplate;
			if (genericParameterIndex >= 0)
				paramTypeTemplate = TypeFactory.resolveType(genericParameterTypes[genericParameterIndex]);
			else
				paramTypeTemplate = TypeFactory.resolveType(parameter.getParameterizedType());

			parameters.add(new ParameterFacade<>(parameter, parameters.size(), this, paramTypeTemplate));
		}

		declaringTypeTemplate.postInit(() -> parameterizedElement.apply(declaringTypeTemplate.typeParameters()));
	}

	@Override
	void seal() {
		parameterizedElement.seal(annotatedElement, this);
		super.seal();
	}

	@Override
	public IuExecutableKey getKey() {
		return key;
	}

	@Override
	public Map<String, TypeFacade<?, ?>> typeParameters() {
		checkSealed();
		return parameterizedElement.typeParameters();
	}

	@Override
	public TypeFacade<?, ?> typeParameter(String name) {
		checkSealed();
		return parameterizedElement.typeParameter(name);
	}

	@Override
	public List<ParameterFacade<?>> parameters() {
		return parameters;
	}

	@Override
	public String toString() {
		if (declaringType == null)
			return "<uninitialized>";

		var sb = new StringBuilder();
		sb.append(TypeUtils.printType(declaringType.deref()));
		if (this instanceof IuNamedElement named)
			sb.append('.').append(named.name());
		sb.append('(');
		var l = sb.length();
		for (var p : parameters) {
			if (l < sb.length())
				sb.append(',');
			sb.append(TypeUtils.printType(p.type().deref()));
		}
		sb.append(')');
		return sb.toString();
	}

}