ClassElement.java
/*
* Copyright 2017-2020 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.inject.ast;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationUtil;
import io.micronaut.core.annotation.Creator;
import io.micronaut.core.annotation.Experimental;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.type.DefaultArgument;
import io.micronaut.core.util.ArgumentUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.inject.ast.annotation.MutableAnnotationMetadataDelegate;
import io.micronaut.inject.ast.beans.BeanElementBuilder;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
import static io.micronaut.inject.writer.BeanDefinitionVisitor.PROXY_SUFFIX;
/**
* Stores data about an element that references a class.
*
* @author James Kleeh
* @author graemerocher
* @since 1.0
*/
public interface ClassElement extends TypedElement {
/**
* Constant for an empty class element array.
*
* @since 3.1.0
*/
ClassElement[] ZERO_CLASS_ELEMENTS = new ClassElement[0];
/**
* Returns the type annotations.
* Added by:
* - The declaration of the type variable {@link java.lang.annotation.ElementType#TYPE_PARAMETER}
* - The use of the type {@link java.lang.annotation.ElementType#TYPE}
* @return the type annotations
* @since 4.0.0
*/
@Experimental
@NonNull
default MutableAnnotationMetadataDelegate<AnnotationMetadata> getTypeAnnotationMetadata() {
return (MutableAnnotationMetadataDelegate<AnnotationMetadata>) MutableAnnotationMetadataDelegate.EMPTY;
}
/**
* Tests whether one type is assignable to another.
*
* @param type The type to check
* @return {@code true} if and only if this type is assignable to the second
*/
boolean isAssignable(String type);
/**
* In this case of calling {@link #getTypeArguments()} a returned {@link ClassElement} may represent a type variable
* in which case this method will return {@code true}.
*
* @return Is this type a type variable.
* @since 3.0.0
*/
default boolean isTypeVariable() {
return false;
}
/**
* @return Whether this is a generic placeholder.
* @see GenericPlaceholderElement
* @since 3.1.0
*/
@Experimental
default boolean isGenericPlaceholder() {
return this instanceof GenericPlaceholderElement;
}
/**
* @return Whether this is a wildcard.
* @see WildcardElement
*/
@Experimental
default boolean isWildcard() {
return this instanceof WildcardElement;
}
/**
* Is raw type.
* @return true if the type is raw
* @since 4.0.0
*/
@Experimental
default boolean isRawType() {
return false;
}
/**
* Tests whether one type is assignable to another.
*
* @param type The type to check
* @return {@code true} if and only if this type is assignable to the second
* @since 2.3.0
*/
default boolean isAssignable(ClassElement type) {
return isAssignable(type.getName());
}
/**
* Whether this element is an {@link Optional}.
*
* @return Is this element an optional
* @since 2.3.0
*/
default boolean isOptional() {
return isAssignable(Optional.class) || isAssignable(OptionalLong.class) || isAssignable(OptionalDouble.class) || isAssignable(OptionalInt.class);
}
/**
* Checks whether the bean type is a container type.
*
* @return Whether the type is a container type like {@link Iterable}.
* @since 4.0.0
*/
default boolean isContainerType() {
return DefaultArgument.CONTAINER_TYPES.contains(getName());
}
/**
* Gets optional value type.
*
* @return the value type
* @since 4.0.0
*/
default Optional<ClassElement> getOptionalValueType() {
return Optional.empty();
}
/**
* This method will return the name of the underlying type automatically unwrapping in the case of an optional
* or wrapped representation of the type.
*
* @return Returns the canonical name of the type.
* @since 2.3.0
*/
default String getCanonicalName() {
if (isOptional()) {
return getFirstTypeArgument().map(ClassElement::getName).orElse(Object.class.getName());
} else {
return getName();
}
}
/**
* @return Whether this element is a record
* @since 2.1.0
*/
default boolean isRecord() {
return false;
}
/**
* Is this type an inner class.
*
* @return True if it is an inner class
* @since 2.1.2
*/
default boolean isInner() {
return false;
}
/**
* Whether this element is an enum.
*
* @return True if it is an enum
*/
default boolean isEnum() {
return this instanceof EnumElement;
}
/**
* @return True if the class represents a proxy
*/
default boolean isProxy() {
return getSimpleName().endsWith(PROXY_SUFFIX);
}
/**
* Find and return a single primary constructor. If more than constructor candidate exists, then return empty unless a
* constructor is found that is annotated with either {@link io.micronaut.core.annotation.Creator} or {@link AnnotationUtil#INJECT}.
*
* @return The primary constructor if one is present
*/
default @NonNull Optional<MethodElement> getPrimaryConstructor() {
Optional<MethodElement> staticCreator = findStaticCreator();
if (staticCreator.isPresent()) {
return staticCreator;
}
if (isInner() && !isStatic()) {
// only static inner classes can be constructed
return Optional.empty();
}
List<ConstructorElement> constructors = getAccessibleConstructors();
if (constructors.isEmpty()) {
return Optional.empty();
}
if (constructors.size() == 1) {
return Optional.of(constructors.get(0));
}
Optional<ConstructorElement> annotatedConstructor = constructors.stream()
.filter(c -> c.hasStereotype(AnnotationUtil.INJECT) || c.hasStereotype(Creator.class))
.findFirst();
if (annotatedConstructor.isPresent()) {
return annotatedConstructor.map(c -> c);
}
return constructors.stream()
.filter(io.micronaut.inject.ast.Element::isPublic)
.<MethodElement>map(c -> c)
.findFirst();
}
/**
* Find and return a single default constructor. A default constructor is one
* without arguments that is accessible.
*
* @return The default constructor if one is present
*/
default Optional<MethodElement> getDefaultConstructor() {
Optional<MethodElement> staticCreator = findDefaultStaticCreator();
if (staticCreator.isPresent()) {
return staticCreator;
}
if (isInner() && !isStatic()) {
// only static inner classes can be constructed
return Optional.empty();
}
List<ConstructorElement> constructors = getAccessibleConstructors()
.stream()
.filter(ctor -> ctor.getParameters().length == 0).toList();
if (constructors.isEmpty()) {
return Optional.empty();
}
if (constructors.size() == 1) {
return Optional.of(constructors.get(0));
}
return constructors.stream()
.filter(Element::isPublic)
.<MethodElement>map(c -> c)
.findFirst();
}
/**
* Find and return a single primary static creator. If more than creator candidate exists, then return empty unless a static
* creator is found that is annotated with {@link io.micronaut.core.annotation.Creator}.
*
* @return The primary creator if one is present
*/
default Optional<MethodElement> findStaticCreator() {
List<MethodElement> staticCreators = getAccessibleStaticCreators();
if (staticCreators.isEmpty()) {
return Optional.empty();
}
if (staticCreators.size() == 1) {
return Optional.of(staticCreators.get(0));
}
//Can be multiple static @Creator methods. Prefer one with args here. The no arg method (if present) will
//be picked up by findDefaultStaticCreator
List<MethodElement> withArgs = staticCreators.stream().filter(method -> method.getParameters().length > 0).toList();
if (withArgs.size() == 1) {
return Optional.of(withArgs.get(0));
} else {
staticCreators = withArgs;
}
return staticCreators.stream().filter(Element::isPublic).findFirst();
}
/**
* Find and return a single default static creator. A default static creator is one
* without arguments that is accessible.
* *
*
* @return a static creator
* @since 4.0.0
*/
default Optional<MethodElement> findDefaultStaticCreator() {
List<MethodElement> staticCreators = getAccessibleStaticCreators()
.stream()
.filter(c -> c.getParameters().length == 0).toList();
if (staticCreators.isEmpty()) {
return Optional.empty();
}
if (staticCreators.size() == 1) {
return Optional.of(staticCreators.get(0));
}
return staticCreators.stream().filter(Element::isPublic).findFirst();
}
/**
* Find accessible constructors.
*
* @return accessible constructors
* @since 4.0.0
*/
@NonNull
default List<ConstructorElement> getAccessibleConstructors() {
return getEnclosedElements(ElementQuery.CONSTRUCTORS)
.stream()
.filter(ctor -> !ctor.isPrivate())
.toList();
}
/**
* Get accessible static creators.
* A static creator is a static method annotated with {@link Creator} that can be used to create the class.
* For enums "valueOf" is picked as a static creator.
*
* @return static creators
* @since 4.0.0
*/
@NonNull
default List<MethodElement> getAccessibleStaticCreators() {
List<MethodElement> creators = getEnclosedElements(ElementQuery.ALL_METHODS
.onlyDeclared()
.onlyStatic()
.onlyAccessible()
.annotated(annotationMetadata -> annotationMetadata.hasStereotype(Creator.class))
)
.stream()
.filter(method -> method.getReturnType().isAssignable(this))
.toList();
if (creators.isEmpty() && isEnum()) {
return getEnclosedElements(ElementQuery.ALL_METHODS
.named("valueOf")
.onlyStatic()
.onlyAccessible()
)
.stream()
.filter(method -> method.getReturnType().isAssignable(this))
.toList();
}
return creators;
}
/**
* Returns the super type of this element or empty if the element has no super type.
*
* @return An optional of the super type
*/
default Optional<ClassElement> getSuperType() {
return Optional.empty();
}
/**
* @return The interfaces implemented by this class element
*/
default Collection<ClassElement> getInterfaces() {
return Collections.emptyList();
}
@NonNull
@Override
default ClassElement getType() {
return this;
}
/**
* The simple name without the package name.
*
* @return The simple name
*/
@Override
default String getSimpleName() {
return NameUtils.getSimpleName(getName());
}
/**
* The package name.
*
* @return The package name
*/
default String getPackageName() {
return NameUtils.getPackageName(getName());
}
/**
* The package name.
*
* @return The package name
* @since 3.0.0
*/
default PackageElement getPackage() {
return PackageElement.of(getPackageName());
}
/**
* Returns the bean properties (getters and setters) for this class element.
*
* @return The bean properties for this class element
*/
@NonNull
default List<PropertyElement> getBeanProperties() {
return Collections.emptyList();
}
/**
* Returns the synthetic bean properties. The properties where one of the methods (getter or setter)
* is synthetic - not user defined but created by the compiler.
*
* @return The bean properties for this class element
* @since 4.0.0
*/
@NonNull
default List<PropertyElement> getSyntheticBeanProperties() {
return Collections.emptyList();
}
/**
* Returns the bean properties (getters and setters) for this class element based on custom configuration.
*
* @param propertyElementQuery The configuration
* @return The bean properties for this class element
* @since 4.0.0
*/
@NonNull
default List<PropertyElement> getBeanProperties(@NonNull PropertyElementQuery propertyElementQuery) {
return Collections.emptyList();
}
/**
* Return all the fields of this class element.
*
* @return The fields
*/
@NonNull
default List<FieldElement> getFields() {
return getEnclosedElements(ElementQuery.ALL_FIELDS);
}
/**
* Find an instance/static field with a name in this class, super class or an interface.
*
* @param name The field name
* @return The field
* @since 4.0.0
*/
@Experimental
@NonNull
default Optional<FieldElement> findField(String name) {
return getEnclosedElement(ElementQuery.ALL_FIELDS.named(name));
}
/**
* Find an instance/static method with a name in this class, super class or an interface.
*
* @return The methods
* @since 4.0.0
*/
@NonNull
default List<MethodElement> getMethods() {
return getEnclosedElements(ElementQuery.ALL_METHODS);
}
/**
* Find a method with a name.
*
* @param name The method name
* @return The method
* @since 4.0.0
*/
@NonNull
@Experimental
default Optional<MethodElement> findMethod(String name) {
return getEnclosedElement(ElementQuery.ALL_METHODS.named(name));
}
/**
* Return the elements that match the given query.
*
* @param query The query to use.
* @param <T> The element type
* @return The fields
* @since 2.3.0
*/
@NonNull
default <T extends Element> List<T> getEnclosedElements(@NonNull ElementQuery<T> query) {
return Collections.emptyList();
}
/**
* Returns the enclosing type if {@link #isInner()} return {@code true}.
*
* @return The enclosing type if any
* @since 3.0.0
*/
default Optional<ClassElement> getEnclosingType() {
return Optional.empty();
}
/**
* Return the first enclosed element matching the given query.
*
* @param query The query to use.
* @param <T> The element type
* @return The fields
* @since 2.3.0
*/
default <T extends Element> Optional<T> getEnclosedElement(@NonNull ElementQuery<T> query) {
List<T> enclosedElements = getEnclosedElements(query);
if (!enclosedElements.isEmpty()) {
return Optional.of(enclosedElements.iterator().next());
}
return Optional.empty();
}
/**
* @return Whether the class element is an interface
*/
default boolean isInterface() {
return false;
}
/**
* @return Whether the type is iterable (either an array or an {@link Iterable})
*/
default boolean isIterable() {
return isArray() || isAssignable(Iterable.class);
}
/**
* The list of type arguments bound to this type, or an empty list if there are no type arguments or this is a raw
* type.
* <p>
* Note that for compatibility reasons, this method is inconsistent with {@link #getTypeArguments()}. In particular,
* this method reflects the <i>declaration</i> type: If there is a {@code class Test<T> { T field; }}, this method
* will return {@code T} as the field type, even if the field type was obtained through a {@code Test<String>}.
*
* @return The list of type arguments, in the same order as {@link #getDeclaredGenericPlaceholders()}. Must be empty or
* of the same length as {@link #getDeclaredGenericPlaceholders()}.
* @since 3.1.0
*/
@NonNull
@Experimental
default List<? extends ClassElement> getBoundGenericTypes() {
return new ArrayList<>(getTypeArguments().values());
}
/**
* The type arguments declared on the raw class. Independent of the actual
* {@link #getBoundGenericTypes() bound type arguments}.
*
* <p>This method will resolve the generic placeholders defined of the declaring class, if any.
* </p>
*
* <p>For example {@code List<String>} will result a single placeholder called {@code E} of type {@link Object}.</p>
*
* @return The type arguments declared on this class.
* @since 3.1.0
*/
@NonNull
@Experimental
default List<? extends GenericPlaceholderElement> getDeclaredGenericPlaceholders() {
return Collections.emptyList();
}
/**
* Get a {@link ClassElement} instance corresponding to this type, but without any type arguments bound. For
* {@code List<String>}, this returns {@code List}.
*
* @return The raw class of this potentially parameterized type.
* @since 3.1.0
*/
@NonNull
@Experimental
default ClassElement getRawClassElement() {
return withTypeArguments(Collections.emptyList());
}
/**
* Get a {@link ClassElement} instance corresponding to this type, but with the given type arguments. This is the best
* effort ��� implementations may only support {@link ClassElement}s that come from the same visitor context, and
* other {@link ClassElement}s only to a limited degree.
*
* @param typeArguments The new type arguments.
* @return A {@link ClassElement} of the same raw class with the new type arguments.
* @throws UnsupportedOperationException If any of the given type arguments are unsupported.
* @deprecated replaced with {@link #withTypeArguments(Collection)} for consistent API.
*/
@NonNull
@Experimental
@Deprecated(since = "4", forRemoval = true)
default ClassElement withBoundGenericTypes(@NonNull List<? extends ClassElement> typeArguments) {
return withTypeArguments((Collection<ClassElement>) typeArguments);
}
/**
* Perform a fold operation on the type arguments (type arguments, wildcard bounds, resolved via {@link #getBoundGenericTypes()}), and then on this
* type. For {@code List<? extends String>}, this returns {@code f(List<f(? extends f(String))>)}. The bounds of
* type variables are not folded.
*
* <p>
* {@code null} has special meaning here. Returning {@code null} from a fold operation will try to make the
* surrounding type a raw type. For example, for {@code Map<String, Object>}, returning {@code null} for the fold
* on {@code Object} will lead to the parameterized {@code Map<String, null>} type being replaced by {@code Map}.
* </p>
*
* <p>This also means that this method may return {@code null} if the top-level fold operation returned {@code null}.</p>
*
* @param fold The fold operation to apply recursively to all component types.
* @return The folded type.
* @since 3.1.0
*/
@Experimental
@Nullable
default ClassElement foldBoundGenericTypes(@NonNull Function<ClassElement, ClassElement> fold) {
List<ClassElement> typeArgs = getBoundGenericTypes().stream().map(arg -> arg.foldBoundGenericTypes(fold)).toList();
if (typeArgs.contains(null)) {
typeArgs = Collections.emptyList();
}
return fold.apply(withTypeArguments(typeArgs));
}
/**
* Get the type arguments for the given type name.
*
* @param type The type to retrieve type arguments for
* @return The type arguments for this class element
* @since 1.1.1
*/
@NonNull
default Map<String, ClassElement> getTypeArguments(@NonNull String type) {
ArgumentUtils.requireNonNull("type", type);
return getAllTypeArguments().getOrDefault(type, Collections.emptyMap());
}
/**
* Get the type arguments for the given type name.
*
* @param type The type to retrieve type arguments for
* @return The type arguments for this class element
*/
@NonNull
default Map<String, ClassElement> getTypeArguments(@NonNull Class<?> type) {
ArgumentUtils.requireNonNull("type", type);
return getTypeArguments(type.getName());
}
/**
* @return The type arguments for this class element
*/
@NonNull
default Map<String, ClassElement> getTypeArguments() {
return Collections.emptyMap();
}
/**
* Builds a map of all the type parameters for a class, its super classes and interfaces.
* The resulting map contains the name of the class to the map of the resolved generic types.
*
* @return The type arguments for this class element
*/
@NonNull
default Map<String, Map<String, ClassElement>> getAllTypeArguments() {
Map<String, Map<String, ClassElement>> result = new LinkedHashMap<>();
Stream.concat(
getInterfaces().stream(),
getSuperType().stream()
).map(ClassElement::getAllTypeArguments).forEach(result::putAll);
result.put(getName(), getTypeArguments());
return result;
}
/**
* @return The first type argument
*/
default Optional<ClassElement> getFirstTypeArgument() {
return getTypeArguments().values().stream().findFirst();
}
/**
* Tests whether one type is assignable to another.
*
* @param type The type to check
* @return {@code true} if and only if the type is assignable to the second
*/
default boolean isAssignable(Class<?> type) {
return isAssignable(type.getName());
}
/**
* Convert the class element to an element for the same type, but representing an array.
* Do not mutate the existing instance. Create a new instance instead.
*
* @return A new class element
*/
@NonNull
ClassElement toArray();
/**
* Dereference a class element denoting an array type by converting it to its element type.
* Do not mutate the existing instance. Create a new instance instead.
*
* @return A new class element
* @throws IllegalStateException if this class element doesn't denote an array type
*/
@NonNull
ClassElement fromArray();
/**
* This method adds an associated bean using this class element as the originating element.
*
* <p>Note that this method can only be called on classes being directly compiled by Micronaut. If the ClassElement is
* loaded from pre-compiled code an {@link UnsupportedOperationException} will be thrown.</p>
*
* @param type The type of the bean
* @return A bean builder
*/
@NonNull
default BeanElementBuilder addAssociatedBean(@NonNull ClassElement type) {
throw new UnsupportedOperationException("Element of type [" + getClass() + "] does not support adding associated beans at compilation time");
}
@Override
default ClassElement withAnnotationMetadata(AnnotationMetadata annotationMetadata) {
return (ClassElement) TypedElement.super.withAnnotationMetadata(annotationMetadata);
}
/**
* Copies this element and overrides its type arguments.
*
* @param typeArguments The type arguments
* @return A new element
* @since 4.0.0
*/
@NonNull
default ClassElement withTypeArguments(Map<String, ClassElement> typeArguments) {
throw new UnsupportedOperationException("Element of type [" + getClass() + "] does not support copy constructor");
}
/**
* Copies this element and overrides its type arguments.
* Variation of {@link #withTypeArguments(Map)} that doesn't require type argument names.
*
* @param typeArguments The type arguments
* @return A new element
* @since 4.0.0
*/
@NonNull
default ClassElement withTypeArguments(@NonNull Collection<ClassElement> typeArguments) {
if (typeArguments.isEmpty()) {
// Allow to eliminate all arguments
return withTypeArguments(Collections.emptyMap());
}
Set<String> genericNames = getTypeArguments().keySet();
if (genericNames.size() != typeArguments.size()) {
throw new IllegalStateException("Expected to have: " + genericNames.size() + " type arguments! Got: " + typeArguments.size());
}
Map<String, ClassElement> boundByName = CollectionUtils.newLinkedHashMap(typeArguments.size());
Iterator<String> keys = genericNames.iterator();
Iterator<? extends ClassElement> args = typeArguments.iterator();
while (keys.hasNext() && args.hasNext()) {
boundByName.put(keys.next(), args.next());
}
return withTypeArguments(boundByName);
}
/**
* Create a class element for the given simple type.
*
* @param type The type
* @return The class element
*/
@NonNull
static ClassElement of(@NonNull Class<?> type) {
return new ReflectClassElement(
Objects.requireNonNull(type, "Type cannot be null")
);
}
/**
* Create a class element for the given complex type.
*
* @param type The type
* @return The class element
*/
@Experimental
@NonNull
static ClassElement of(@NonNull Type type) {
Objects.requireNonNull(type, "Type cannot be null");
if (type instanceof Class<?> aClass) {
return new ReflectClassElement(aClass);
} else if (type instanceof TypeVariable<?> typeVariable) {
return new ReflectGenericPlaceholderElement(typeVariable, 0);
} else if (type instanceof WildcardType wildcardType) {
return new ReflectWildcardElement(wildcardType);
} else if (type instanceof ParameterizedType pType) {
if (pType.getOwnerType() != null) {
throw new UnsupportedOperationException("Owner types are not supported");
}
return new ReflectClassElement(ReflectTypeElement.getErasure(type)) {
@NonNull
@Override
public List<? extends ClassElement> getBoundGenericTypes() {
return Arrays.stream(pType.getActualTypeArguments())
.map(ClassElement::of)
.toList();
}
};
} else if (type instanceof GenericArrayType genericArrayType) {
return of(genericArrayType.getGenericComponentType()).toArray();
} else {
throw new IllegalArgumentException("Bad type: " + type.getClass().getName());
}
}
/**
* Create a class element for the given simple type.
*
* @param type The type
* @param annotationMetadata The annotation metadata
* @param typeArguments The type arguments
* @return The class element
* @since 2.4.0
*/
@NonNull
static ClassElement of(@NonNull Class<?> type,
@NonNull AnnotationMetadata annotationMetadata,
@NonNull Map<String, ClassElement> typeArguments) {
Objects.requireNonNull(annotationMetadata, "Annotation metadata cannot be null");
Objects.requireNonNull(typeArguments, "Type arguments cannot be null");
return new ReflectClassElement(
Objects.requireNonNull(type, "Type cannot be null")
) {
@Override
public AnnotationMetadata getAnnotationMetadata() {
return annotationMetadata;
}
@Override
public Map<String, ClassElement> getTypeArguments() {
return Collections.unmodifiableMap(typeArguments);
}
@NonNull
@Override
public List<? extends ClassElement> getBoundGenericTypes() {
return getDeclaredGenericPlaceholders().stream()
.map(tv -> typeArguments.get(tv.getVariableName()))
.toList();
}
};
}
/**
* Create a class element for the given simple type.
*
* @param typeName The type
* @return The class element
*/
@Internal
@NonNull
static ClassElement of(@NonNull String typeName) {
return new SimpleClassElement(typeName);
}
/**
* Create a class element for the given simple type.
*
* @param typeName The type
* @param isInterface Is the type an interface
* @param annotationMetadata The annotation metadata
* @return The class element
*/
@Internal
@NonNull
static ClassElement of(@NonNull String typeName, boolean isInterface, @Nullable AnnotationMetadata annotationMetadata) {
return new SimpleClassElement(typeName, isInterface, annotationMetadata);
}
/**
* Create a class element for the given simple type.
*
* @param typeName The type
* @param isInterface Is the type an interface
* @param annotationMetadata The annotation metadata
* @param typeArguments The type arguments
* @return The class element
*/
@Internal
@NonNull
static ClassElement of(@NonNull String typeName, boolean isInterface, @Nullable AnnotationMetadata annotationMetadata, Map<String, ClassElement> typeArguments) {
return new SimpleClassElement(typeName, isInterface, annotationMetadata);
}
}