All Downloads are FREE. Search and download functionalities are using the official Maven repository.

keycloakjar.org.springframework.aot.hint.predicate.ReflectionHintsPredicates Maven / Gradle / Ivy

There is a newer version: 7.21.1
Show newest version
/*
 * Copyright 2002-2022 the original author or 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 org.springframework.aot.hint.predicate;

import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;

import org.springframework.aot.hint.ExecutableHint;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeHint;
import org.springframework.aot.hint.TypeReference;
import org.springframework.core.MethodIntrospector;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

/**
 * Generator of {@link ReflectionHints} predicates, testing whether the given hints
 * match the expected behavior for reflection.
 *
 * @author Brian Clozel
 * @author Stephane Nicoll
 * @since 6.0
 */
public class ReflectionHintsPredicates {

	ReflectionHintsPredicates() {
	}


	/**
	 * Return a predicate that checks whether a reflection hint is registered for the given type.
	 * 

The returned type exposes additional methods that refine the predicate behavior. * @param typeReference the type * @return the {@link RuntimeHints} predicate */ public TypeHintPredicate onType(TypeReference typeReference) { Assert.notNull(typeReference, "'typeReference' must not be null"); return new TypeHintPredicate(typeReference); } /** * Return a predicate that checks whether a reflection hint is registered for the given type. *

The returned type exposes additional methods that refine the predicate behavior. * @param type the type * @return the {@link RuntimeHints} predicate */ public TypeHintPredicate onType(Class type) { Assert.notNull(type, "'type' must not be null"); return new TypeHintPredicate(TypeReference.of(type)); } /** * Return a predicate that checks whether a reflection hint is registered for the given constructor. * By default, both introspection and invocation hints match. *

The returned type exposes additional methods that refine the predicate behavior. * @param constructor the constructor * @return the {@link RuntimeHints} predicate */ public ConstructorHintPredicate onConstructor(Constructor constructor) { Assert.notNull(constructor, "'constructor' must not be null"); return new ConstructorHintPredicate(constructor); } /** * Return a predicate that checks whether a reflection hint is registered for the given method. * By default, both introspection and invocation hints match. *

The returned type exposes additional methods that refine the predicate behavior. * @param method the method * @return the {@link RuntimeHints} predicate */ public MethodHintPredicate onMethod(Method method) { Assert.notNull(method, "'method' must not be null"); return new MethodHintPredicate(method); } /** * Return a predicate that checks whether a reflection hint is registered for the method that matches the given selector. * This looks up a method on the given type with the expected name, if unique. * By default, both introspection and invocation hints match. *

The returned type exposes additional methods that refine the predicate behavior. * @param type the type holding the method * @param methodName the method name * @return the {@link RuntimeHints} predicate * @throws IllegalArgumentException if the method cannot be found or if multiple methods are found with the same name. */ public MethodHintPredicate onMethod(Class type, String methodName) { Assert.notNull(type, "'type' must not be null"); Assert.hasText(methodName, "'methodName' must not be empty"); return new MethodHintPredicate(getMethod(type, methodName)); } /** * Return a predicate that checks whether a reflection hint is registered for the method that matches the given selector. * This looks up a method on the given type with the expected name, if unique. * By default, both introspection and invocation hints match. *

The returned type exposes additional methods that refine the predicate behavior. * @param className the name of the class holding the method * @param methodName the method name * @return the {@link RuntimeHints} predicate * @throws ClassNotFoundException if the class cannot be resolved. * @throws IllegalArgumentException if the method cannot be found or if multiple methods are found with the same name. */ public MethodHintPredicate onMethod(String className, String methodName) throws ClassNotFoundException { Assert.hasText(className, "'className' must not be empty"); Assert.hasText(methodName, "'methodName' must not be empty"); return onMethod(Class.forName(className), methodName); } private Method getMethod(Class type, String methodName) { ReflectionUtils.MethodFilter selector = method -> methodName.equals(method.getName()); Set methods = MethodIntrospector.selectMethods(type, selector); if (methods.size() == 1) { return methods.iterator().next(); } else if (methods.size() > 1) { throw new IllegalArgumentException("Found multiple methods named '%s' on class %s".formatted(methodName, type.getName())); } else { throw new IllegalArgumentException("No method named '%s' on class %s".formatted(methodName, type.getName())); } } /** * Return a predicate that checks whether a reflection hint is registered for the field that matches the given selector. * This looks up a field on the given type with the expected name, if present. * By default, unsafe or write access is not considered. *

The returned type exposes additional methods that refine the predicate behavior. * @param type the type holding the field * @param fieldName the field name * @return the {@link RuntimeHints} predicate * @throws IllegalArgumentException if a field cannot be found with the given name. */ public FieldHintPredicate onField(Class type, String fieldName) { Assert.notNull(type, "'type' must not be null"); Assert.hasText(fieldName, "'fieldName' must not be empty"); Field field = ReflectionUtils.findField(type, fieldName); if (field == null) { throw new IllegalArgumentException("No field named '%s' on class %s".formatted(fieldName, type.getName())); } return new FieldHintPredicate(field); } /** * Return a predicate that checks whether a reflection hint is registered for the field that matches the given selector. * This looks up a field on the given type with the expected name, if present. * By default, unsafe or write access is not considered. *

The returned type exposes additional methods that refine the predicate behavior. * @param className the name of the class holding the field * @param fieldName the field name * @return the {@link RuntimeHints} predicate * @throws ClassNotFoundException if the class cannot be resolved. * @throws IllegalArgumentException if a field cannot be found with the given name. */ public FieldHintPredicate onField(String className, String fieldName) throws ClassNotFoundException { Assert.hasText(className, "'className' must not be empty"); Assert.hasText(fieldName, "'fieldName' must not be empty"); return onField(Class.forName(className), fieldName); } /** * Return a predicate that checks whether a reflection hint is registered for the given field. * By default, unsafe or write access is not considered. *

The returned type exposes additional methods that refine the predicate behavior. * @param field the field * @return the {@link RuntimeHints} predicate */ public FieldHintPredicate onField(Field field) { Assert.notNull(field, "'field' must not be null"); return new FieldHintPredicate(field); } public static class TypeHintPredicate implements Predicate { private final TypeReference type; TypeHintPredicate(TypeReference type) { this.type = type; } @Nullable private TypeHint getTypeHint(RuntimeHints hints) { return hints.reflection().getTypeHint(this.type); } @Override public boolean test(RuntimeHints hints) { return getTypeHint(hints) != null; } /** * Refine the current predicate to only match if the given {@link MemberCategory} is present. * @param memberCategory the member category * @return the refined {@link RuntimeHints} predicate */ public Predicate withMemberCategory(MemberCategory memberCategory) { Assert.notNull(memberCategory, "'memberCategory' must not be null"); return this.and(hints -> getTypeHint(hints).getMemberCategories().contains(memberCategory)); } /** * Refine the current predicate to only match if the given {@link MemberCategory categories} are present. * @param memberCategories the member categories * @return the refined {@link RuntimeHints} predicate */ public Predicate withMemberCategories(MemberCategory... memberCategories) { Assert.notEmpty(memberCategories, "'memberCategories' must not be empty"); return this.and(hints -> getTypeHint(hints).getMemberCategories().containsAll(Arrays.asList(memberCategories))); } /** * Refine the current predicate to match if any of the given {@link MemberCategory categories} is present. * @param memberCategories the member categories * @return the refined {@link RuntimeHints} predicate */ public Predicate withAnyMemberCategory(MemberCategory... memberCategories) { Assert.notEmpty(memberCategories, "'memberCategories' must not be empty"); return this.and(hints -> Arrays.stream(memberCategories) .anyMatch(memberCategory -> getTypeHint(hints).getMemberCategories().contains(memberCategory))); } } public abstract static class ExecutableHintPredicate implements Predicate { protected final T executable; protected ExecutableMode executableMode = ExecutableMode.INTROSPECT; ExecutableHintPredicate(T executable) { this.executable = executable; } /** * Refine the current predicate to match for reflection introspection on the current type. * @return the refined {@link RuntimeHints} predicate */ public ExecutableHintPredicate introspect() { this.executableMode = ExecutableMode.INTROSPECT; return this; } /** * Refine the current predicate to match for reflection invocation on the current type. * @return the refined {@link RuntimeHints} predicate */ public ExecutableHintPredicate invoke() { this.executableMode = ExecutableMode.INVOKE; return this; } @Override public boolean test(RuntimeHints runtimeHints) { return (new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())) .withAnyMemberCategory(getPublicMemberCategories()) .and(hints -> Modifier.isPublic(this.executable.getModifiers()))) .or(new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())).withAnyMemberCategory(getDeclaredMemberCategories())) .or(exactMatch()).test(runtimeHints); } abstract MemberCategory[] getPublicMemberCategories(); abstract MemberCategory[] getDeclaredMemberCategories(); abstract Predicate exactMatch(); /** * Indicate whether the specified {@code ExecutableHint} covers the * reflection needs of the specified executable definition. * @return {@code true} if the member matches (same type, name, and parameters), * and the configured {@code ExecutableMode} is compatible */ static boolean includes(ExecutableHint hint, String name, List parameterTypes, ExecutableMode executableModes) { return hint.getName().equals(name) && hint.getParameterTypes().equals(parameterTypes) && (hint.getMode().equals(ExecutableMode.INVOKE) || !executableModes.equals(ExecutableMode.INVOKE)); } } public static class ConstructorHintPredicate extends ExecutableHintPredicate> { ConstructorHintPredicate(Constructor constructor) { super(constructor); } @Override MemberCategory[] getPublicMemberCategories() { if (this.executableMode == ExecutableMode.INTROSPECT) { return new MemberCategory[] { MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS }; } return new MemberCategory[] { MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS }; } @Override MemberCategory[] getDeclaredMemberCategories() { if (this.executableMode == ExecutableMode.INTROSPECT) { return new MemberCategory[] { MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS }; } return new MemberCategory[] { MemberCategory.INVOKE_DECLARED_CONSTRUCTORS }; } @Override Predicate exactMatch() { return hints -> (hints.reflection().getTypeHint(this.executable.getDeclaringClass()) != null) && hints.reflection().getTypeHint(this.executable.getDeclaringClass()).constructors().anyMatch(executableHint -> { List parameters = TypeReference.listOf(this.executable.getParameterTypes()); return includes(executableHint, "", parameters, this.executableMode); }); } } public static class MethodHintPredicate extends ExecutableHintPredicate { MethodHintPredicate(Method method) { super(method); } @Override MemberCategory[] getPublicMemberCategories() { if (this.executableMode == ExecutableMode.INTROSPECT) { return new MemberCategory[] { MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS }; } return new MemberCategory[] { MemberCategory.INVOKE_PUBLIC_METHODS }; } @Override MemberCategory[] getDeclaredMemberCategories() { if (this.executableMode == ExecutableMode.INTROSPECT) { return new MemberCategory[] { MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_METHODS }; } return new MemberCategory[] { MemberCategory.INVOKE_DECLARED_METHODS }; } @Override Predicate exactMatch() { return hints -> (hints.reflection().getTypeHint(this.executable.getDeclaringClass()) != null) && hints.reflection().getTypeHint(this.executable.getDeclaringClass()).methods().anyMatch(executableHint -> { List parameters = TypeReference.listOf(this.executable.getParameterTypes()); return includes(executableHint, this.executable.getName(), parameters, this.executableMode); }); } } public static class FieldHintPredicate implements Predicate { private final Field field; FieldHintPredicate(Field field) { this.field = field; } @Override public boolean test(RuntimeHints runtimeHints) { TypeHint typeHint = runtimeHints.reflection().getTypeHint(this.field.getDeclaringClass()); if (typeHint == null) { return false; } return memberCategoryMatch(typeHint) || exactMatch(typeHint); } private boolean memberCategoryMatch(TypeHint typeHint) { if (Modifier.isPublic(this.field.getModifiers())) { return typeHint.getMemberCategories().contains(MemberCategory.PUBLIC_FIELDS) || typeHint.getMemberCategories().contains(MemberCategory.DECLARED_FIELDS); } else { return typeHint.getMemberCategories().contains(MemberCategory.DECLARED_FIELDS); } } private boolean exactMatch(TypeHint typeHint) { return typeHint.fields().anyMatch(fieldHint -> this.field.getName().equals(fieldHint.getName())); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy