ParameterizedElement.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.GenericDeclaration;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import edu.iu.type.IuParameterizedElement;
import edu.iu.type.IuReferenceKind;
/**
* Implements the facade view of an {@link IuParameterizedElement} as a
* delegating mix-in.
*/
final class ParameterizedElement implements ParameterizedFacade {
private Map<String, TypeFacade<?, ?>> typeArguments = new LinkedHashMap<>();
private Map<String, TypeFacade<?, ?>> typeParameters;
/**
* Facade constructor.
*/
ParameterizedElement() {
}
/**
* Applies type arguments.
*
* <p>
* This is typically done after sealing original type parameters to apply the
* resulting parameters as arguments to the next reference.
* </p>
*
* @param typeArguments Type arguments to apply. Expects arguments to be applied
* in highest-order last; type arguments matching the name
* of a previously applied argument will be overridden.
*/
void apply(Map<String, TypeFacade<?, ?>> typeArguments) {
if (this.typeArguments == null)
throw new IllegalStateException("sealed");
this.typeArguments.putAll(typeArguments);
}
/**
* Applies a type argument from a parameterized type.
*
* @param referrer element referring to the parameterized type,
* typically its {@link TypeTemplate}.
* @param typeVariable type variable declared by
* {@link ParameterizedType#getRawType() raw type
* erasure}.
* @param actualTypeArgument from
* {@link ParameterizedType#getActualTypeArguments()}
* relative to position of {@code typeVariable}.
*/
void apply(AnnotatedElementBase<?> referrer, TypeVariable<?> typeVariable, Type actualTypeArgument) {
if (this.typeArguments == null)
throw new IllegalStateException("sealed");
final var name = typeVariable.getName();
final var typeArgumentTemplate = TypeFactory.resolveType(actualTypeArgument);
this.typeArguments.put(name,
new TypeFacade<>(typeArgumentTemplate, referrer, IuReferenceKind.TYPE_PARAM, name));
}
/**
* Seals type parameters from incoming arguments based on a generic declaration.
*
* @param genericDeclaration source element capable of declaring type parameters
* @param referrer referring element to use with any type parameters
* generated from {@code genericDeclaration}
*/
void seal(GenericDeclaration genericDeclaration, AnnotatedElementBase<?> referrer) {
if (typeParameters != null)
throw new IllegalStateException("already sealed");
final var typeArguments = this.typeArguments;
this.typeArguments = null;
final var typeVariables = genericDeclaration.getTypeParameters();
if (typeVariables.length == 0) {
this.typeParameters = Collections.emptyMap();
return;
}
final IuReferenceKind kind;
if (genericDeclaration instanceof Class)
kind = IuReferenceKind.TYPE_PARAM;
else if (genericDeclaration instanceof Method)
kind = IuReferenceKind.METHOD_PARAM;
else
kind = IuReferenceKind.CONSTRUCTOR_PARAM;
// Step through all incoming type parameters
final Map<String, TypeFacade<?, ?>> typeParameters = new LinkedHashMap<>();
for (var typeVariable : typeVariables) {
final var typeVariableName = typeVariable.getName();
var typeArgument = typeArguments.get(typeVariableName);
if (typeArgument != null) {
while (typeArgument.deref() instanceof TypeVariable<?> argVariable) {
final var derefArgVar = typeArguments.get(argVariable.getName());
if (derefArgVar == null // if unresolved or
|| derefArgVar == typeArgument) // self-reference
break; // then keep variable and defer to bounds
else // push dereferenced argument and check again
typeArgument = derefArgVar;
}
typeParameters.put(typeVariableName, typeArgument);
} else
typeParameters.put(typeVariableName,
new TypeFacade<>(TypeFactory.resolveType(typeVariable), referrer, kind, typeVariableName));
}
this.typeParameters = typeParameters;
}
@Override
public Map<String, TypeFacade<?, ?>> typeParameters() {
if (typeParameters == null)
throw new IllegalStateException("not sealed");
return typeParameters;
}
@Override
public TypeFacade<?, ?> typeParameter(String name) {
return typeParameters().get(name);
}
}