TypeFacade.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.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayDeque;
import java.util.Map;
import java.util.Queue;
import java.util.function.Function;
import java.util.function.Predicate;

import edu.iu.type.InstanceReference;
import edu.iu.type.IuConstructor;
import edu.iu.type.IuField;
import edu.iu.type.IuMethod;
import edu.iu.type.IuProperty;
import edu.iu.type.IuReferenceKind;
import edu.iu.type.IuType;
import edu.iu.type.IuTypeReference;

/**
 * Facade implementation of {@link IuType}.
 * 
 * <p>
 * Always includes a non-null {@link #reference()} for which
 * {@link IuTypeReference#referent()} == {@code this}.
 * </p>
 * 
 * <p>
 * Delegates most lookups to a {@link TypeTemplate}, but propagates independent
 * resolution of type parameters based on arguments provided via referrer, which
 * may or may not be the same {@link TypeTemplate} this facade delegates to.
 * </p>
 * 
 * @param <D> declaring type
 * @param <T> generic type
 */
final class TypeFacade<D, T> extends ElementBase implements IuType<D, T>, ParameterizedFacade {

	/**
	 * Holds a reference to the template this facade delegates to.
	 */
	final TypeTemplate<D, T> template;

	/**
	 * Parameterized element mix-in.
	 * 
	 * <p>
	 * May be used by related components to apply type arguments to managed
	 * instances before sealing. Once sealed, the public interface (i.e.,
	 * {@link #typeParameter(String)}) is preferred.
	 * </p>
	 */
	final ParameterizedElement parameterizedElement = new ParameterizedElement();

	private final TypeReference<T, ?> reference;
	private Iterable<TypeFacade<?, ? super T>> hierarchy;

	/**
	 * Constructor for a non-named, non-indexed reference.
	 * 
	 * @param template      type template
	 * @param referrer      referrer element
	 * @param referenceKind reference kind
	 */
	TypeFacade(TypeTemplate<D, T> template, ElementBase referrer, IuReferenceKind referenceKind) {
		this(template, a -> new TypeReference<>(referenceKind, referrer, a));
	}

	/**
	 * Constructor for a named reference.
	 * 
	 * @param template      type template
	 * @param referrer      referrer element
	 * @param referenceKind reference kind
	 * @param referenceName reference name
	 */
	TypeFacade(TypeTemplate<D, T> template, ElementBase referrer, IuReferenceKind referenceKind, String referenceName) {
		this(template, a -> new TypeReference<>(referenceKind, referrer, a, referenceName));
	}

	/**
	 * Constructor for an indexed reference.
	 * 
	 * @param template       type template
	 * @param referrer       referrer element
	 * @param referenceKind  reference kind
	 * @param referenceIndex reference index
	 */
	TypeFacade(TypeTemplate<D, T> template, ElementBase referrer, IuReferenceKind referenceKind, int referenceIndex) {
		this(template, a -> new TypeReference<>(referenceKind, referrer, a, referenceIndex));
	}

	private TypeFacade(TypeTemplate<D, T> template, Function<TypeFacade<D, T>, TypeReference<T, ?>> referenceFactory) {
		this.template = template;
		reference = referenceFactory.apply(this);

		var referrer = reference.referrer();
		referrer.postInit(new Runnable() {
			{ // coverage assertion
				toString();
			}

			public void run() {
				template.postInit(() -> {
					final var templateHierarchy = template.hierarchy();
					Queue<TypeFacade<?, ? super T>> hierarchy = new ArrayDeque<>();
					for (final var templateSuperType : templateHierarchy)
						hierarchy.offer(new TypeFacade<>(templateSuperType.template, TypeFacade.this,
								templateSuperType.reference.kind()));
					TypeFacade.this.hierarchy = hierarchy;

					// i.e. resolve Iterable<T> from Collection<E> implements Iterable<E>
					// template: {T=IuType[E TYPE_PARAM(T) Iterable<E>]}
					parameterizedElement.apply(template.typeParameters());

					// referrer: {E=IuType[E TYPE_PARAM(E) Collection]}
					if (referrer instanceof ParameterizedFacade parameterizedReferrer)
						parameterizedElement.apply(parameterizedReferrer.typeParameters());

					parameterizedElement.seal(template.annotatedElement, template);
					seal();
				});
			}

			@Override
			public String toString() {
				return "TypeFacade-post(" + reference + ')';
			}

		});
	}

	@Override
	public IuTypeReference<T, ?> reference() {
		return reference;
	}

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

	@Override
	public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
		return template.hasAnnotation(annotationType);
	}

	@Override
	public <A extends Annotation> A annotation(Class<A> annotationType) {
		return template.annotation(annotationType);
	}

	@Override
	public Iterable<? extends Annotation> annotations() {
		return template.annotations();
	}

	@Override
	public boolean permitted(Predicate<String> isUserInRole) {
		return template.permitted(isUserInRole);
	}

	@Override
	public boolean permitted() {
		return template.permitted();
	}

	@Override
	public <S> IuType<D, ? extends S> sub(Class<S> subclass) throws ClassCastException {
		return template.sub(subclass);
	}

	@Override
	public Class<T> autoboxClass() {
		return template.autoboxClass();
	}

	@Override
	public T autoboxDefault() {
		return template.autoboxDefault();
	}

	@Override
	public String name() {
		return template.name();
	}

	@Override
	public IuType<?, D> declaringType() {
		return template.declaringType();
	}

	@Override
	public Type deref() {
		return template.deref();
	}

	@Override
	public IuType<D, T> erase() {
		return template.erase();
	}

	@Override
	public Class<T> erasedClass() {
		return template.erasedClass();
	}

	@Override
	public Iterable<TypeFacade<?, ? super T>> hierarchy() {
		checkSealed();
		return hierarchy;
	}

	@Override
	public IuType<?, ? super T> referTo(Type referentType) {
		return TypeUtils.referTo(this, hierarchy(), referentType);
	}

	@Override
	public Iterable<? extends IuType<T, ?>> enclosedTypes() {
		return template.enclosedTypes();
	}

	@Override
	public Iterable<? extends IuConstructor<T>> constructors() {
		return template.constructors();
	}

	@Override
	public Iterable<? extends IuField<? super T, ?>> fields() {
		return template.fields();
	}

	@Override
	public Iterable<? extends IuProperty<? super T, ?>> properties() {
		return template.properties();
	}

	@Override
	public Iterable<? extends IuMethod<? super T, ?>> methods() {
		return template.methods();
	}

	@Override
	public void observe(T instance) {
		template.observe(instance);
	}

	@Override
	public void destroy(T instance) {
		template.destroy(instance);
	}

	@Override
	public Runnable subscribe(InstanceReference<T> instanceReference) {
		return template.subscribe(instanceReference);
	}

	@Override
	public String toString() {
		return "IuType[" + reference + ']';
	}

}