|  | 
| 16 | 16 | package org.springframework.data.repository.aot.generate; | 
| 17 | 17 | 
 | 
| 18 | 18 | import java.lang.reflect.Method; | 
| 19 |  | -import java.lang.reflect.Type; | 
| 20 |  | -import java.lang.reflect.TypeVariable; | 
| 21 |  | -import java.lang.reflect.WildcardType; | 
| 22 | 19 | import java.util.ArrayList; | 
| 23 | 20 | import java.util.Arrays; | 
| 24 | 21 | import java.util.Comparator; | 
| 25 |  | -import java.util.HashSet; | 
| 26 | 22 | import java.util.List; | 
| 27 |  | -import java.util.Set; | 
| 28 | 23 | import java.util.function.Consumer; | 
| 29 |  | -import java.util.function.Predicate; | 
| 30 | 24 | 
 | 
| 31 | 25 | import javax.lang.model.element.Modifier; | 
| 32 | 26 | 
 | 
| 33 | 27 | import org.apache.commons.logging.Log; | 
| 34 | 28 | import org.apache.commons.logging.LogFactory; | 
| 35 | 29 | import org.jspecify.annotations.Nullable; | 
| 36 |  | - | 
| 37 |  | -import org.springframework.core.ResolvableType; | 
| 38 | 30 | import org.springframework.data.projection.ProjectionFactory; | 
| 39 | 31 | import org.springframework.data.repository.core.RepositoryInformation; | 
| 40 | 32 | import org.springframework.data.repository.core.support.RepositoryComposition; | 
| 41 | 33 | import org.springframework.data.repository.core.support.RepositoryFragment; | 
| 42 | 34 | import org.springframework.data.repository.query.QueryMethod; | 
| 43 | 35 | import org.springframework.data.util.Lazy; | 
| 44 |  | -import org.springframework.data.util.TypeInformation; | 
| 45 | 36 | import org.springframework.javapoet.ClassName; | 
| 46 | 37 | import org.springframework.javapoet.FieldSpec; | 
| 47 | 38 | import org.springframework.javapoet.MethodSpec; | 
| @@ -340,210 +331,6 @@ private void contributeMethod(Method method, @Nullable MethodContributorFactory | 
| 340 | 331 | 		generationMetadata.addRepositoryMethod(method, contributor); | 
| 341 | 332 | 	} | 
| 342 | 333 | 
 | 
| 343 |  | -	/** | 
| 344 |  | -	 * Value object to determine whether generics in a given {@link Method} can be resolved. Resolvable generics are e.g. | 
| 345 |  | -	 * declared on the method level (unbounded type variables, type variables using class boundaries). Considers | 
| 346 |  | -	 * collections and map types. | 
| 347 |  | -	 * <p> | 
| 348 |  | -	 * Considers resolvable: | 
| 349 |  | -	 * <ul> | 
| 350 |  | -	 * <li>Unbounded method-level type parameters {@code <T> T foo(Class<T>)}</li> | 
| 351 |  | -	 * <li>Bounded method-level type parameters that resolve to a class | 
| 352 |  | -	 * {@code <T extends Serializable> T foo(Class<T>)}</li> | 
| 353 |  | -	 * <li>Simple references to interface variables {@code T foo(), List<T> foo(…)}</li> | 
| 354 |  | -	 * <li>Unbounded wildcards {@code User foo(GeoJson<?>)}</li> | 
| 355 |  | -	 * </ul> | 
| 356 |  | -	 * Considers non-resolvable: | 
| 357 |  | -	 * <ul> | 
| 358 |  | -	 * <li>Parametrized bounds referring to known variables on method-level type parameters | 
| 359 |  | -	 * {@code <P extends T> T foo(Class<T>), List<? super T> foo()}</li> | 
| 360 |  | -	 * <li>Generally unresolvable generics</li> | 
| 361 |  | -	 * </ul> | 
| 362 |  | -	 * </p> | 
| 363 |  | -	 * | 
| 364 |  | -	 * @author Mark Paluch | 
| 365 |  | -	 */ | 
| 366 |  | -	record ResolvableGenerics(Method method, Class<?> implClass, Set<Type> resolvableTypeVariables, | 
| 367 |  | -			Set<Type> unwantedMethodVariables) { | 
| 368 |  | - | 
| 369 |  | -		/** | 
| 370 |  | -		 * Create a new {@code ResolvableGenerics} object for the given {@link Method}. | 
| 371 |  | -		 * | 
| 372 |  | -		 * @param method | 
| 373 |  | -		 * @return | 
| 374 |  | -		 */ | 
| 375 |  | -		public static ResolvableGenerics of(Method method, Class<?> implClass) { | 
| 376 |  | -			return new ResolvableGenerics(method, implClass, getResolvableTypeVariables(method), | 
| 377 |  | -					getUnwantedMethodVariables(method)); | 
| 378 |  | -		} | 
| 379 |  | - | 
| 380 |  | -		private static Set<Type> getResolvableTypeVariables(Method method) { | 
| 381 |  | - | 
| 382 |  | -			Set<Type> simpleTypeVariables = new HashSet<>(); | 
| 383 |  | - | 
| 384 |  | -			for (TypeVariable<Method> typeParameter : method.getTypeParameters()) { | 
| 385 |  | -				if (isClassBounded(typeParameter.getBounds())) { | 
| 386 |  | -					simpleTypeVariables.add(typeParameter); | 
| 387 |  | -				} | 
| 388 |  | -			} | 
| 389 |  | - | 
| 390 |  | -			return simpleTypeVariables; | 
| 391 |  | -		} | 
| 392 |  | - | 
| 393 |  | -		private static Set<Type> getUnwantedMethodVariables(Method method) { | 
| 394 |  | - | 
| 395 |  | -			Set<Type> unwanted = new HashSet<>(); | 
| 396 |  | - | 
| 397 |  | -			for (TypeVariable<Method> typeParameter : method.getTypeParameters()) { | 
| 398 |  | -				if (!isClassBounded(typeParameter.getBounds())) { | 
| 399 |  | -					unwanted.add(typeParameter); | 
| 400 |  | -				} | 
| 401 |  | -			} | 
| 402 |  | -			return unwanted; | 
| 403 |  | -		} | 
| 404 |  | - | 
| 405 |  | -		/** | 
| 406 |  | -		 * Check whether the {@link Method} has unresolvable generics when being considered in the context of the | 
| 407 |  | -		 * implementation class. | 
| 408 |  | -		 * | 
| 409 |  | -		 * @return | 
| 410 |  | -		 */ | 
| 411 |  | -		public boolean hasUnresolvableGenerics() { | 
| 412 |  | - | 
| 413 |  | -			ResolvableType resolvableType = ResolvableType.forMethodReturnType(method, implClass); | 
| 414 |  | - | 
| 415 |  | -			if (isUnresolvable(resolvableType)) { | 
| 416 |  | -				return true; | 
| 417 |  | -			} | 
| 418 |  | - | 
| 419 |  | -			for (int i = 0; i < method.getParameterCount(); i++) { | 
| 420 |  | -				if (isUnresolvable(ResolvableType.forMethodParameter(method, i, implClass))) { | 
| 421 |  | -					return true; | 
| 422 |  | -				} | 
| 423 |  | -			} | 
| 424 |  | - | 
| 425 |  | -			return false; | 
| 426 |  | -		} | 
| 427 |  | - | 
| 428 |  | -		private boolean isUnresolvable(TypeInformation<?> typeInformation) { | 
| 429 |  | -			return isUnresolvable(typeInformation.toResolvableType()); | 
| 430 |  | -		} | 
| 431 |  | - | 
| 432 |  | -		private boolean isUnresolvable(ResolvableType resolvableType) { | 
| 433 |  | - | 
| 434 |  | -			if (isResolvable(resolvableType)) { | 
| 435 |  | -				return false; | 
| 436 |  | -			} | 
| 437 |  | - | 
| 438 |  | -			if (isUnwanted(resolvableType)) { | 
| 439 |  | -				return true; | 
| 440 |  | -			} | 
| 441 |  | - | 
| 442 |  | -			if (resolvableType.isAssignableFrom(Class.class)) { | 
| 443 |  | -				return isUnresolvable(resolvableType.getGeneric(0)); | 
| 444 |  | -			} | 
| 445 |  | - | 
| 446 |  | -			TypeInformation<?> typeInformation = TypeInformation.of(resolvableType); | 
| 447 |  | -			if (typeInformation.isMap() || typeInformation.isCollectionLike()) { | 
| 448 |  | - | 
| 449 |  | -				for (ResolvableType type : resolvableType.getGenerics()) { | 
| 450 |  | -					if (isUnresolvable(type)) { | 
| 451 |  | -						return true; | 
| 452 |  | -					} | 
| 453 |  | -				} | 
| 454 |  | - | 
| 455 |  | -				return false; | 
| 456 |  | -			} | 
| 457 |  | - | 
| 458 |  | -			if (typeInformation.getActualType() != null && typeInformation.getActualType() != typeInformation) { | 
| 459 |  | -				return isUnresolvable(typeInformation.getRequiredActualType()); | 
| 460 |  | -			} | 
| 461 |  | - | 
| 462 |  | -			return resolvableType.hasUnresolvableGenerics(); | 
| 463 |  | -		} | 
| 464 |  | - | 
| 465 |  | -		private boolean isResolvable(Type[] types) { | 
| 466 |  | - | 
| 467 |  | -			for (Type type : types) { | 
| 468 |  | - | 
| 469 |  | -				if (resolvableTypeVariables.contains(type)) { | 
| 470 |  | -					continue; | 
| 471 |  | -				} | 
| 472 |  | - | 
| 473 |  | -				if (isClass(type)) { | 
| 474 |  | -					continue; | 
| 475 |  | -				} | 
| 476 |  | - | 
| 477 |  | -				return false; | 
| 478 |  | -			} | 
| 479 |  | - | 
| 480 |  | -			return true; | 
| 481 |  | -		} | 
| 482 |  | - | 
| 483 |  | -		private boolean isResolvable(ResolvableType resolvableType) { | 
| 484 |  | - | 
| 485 |  | -			return testGenericType(resolvableType, it -> { | 
| 486 |  | - | 
| 487 |  | -				if (resolvableTypeVariables.contains(it)) { | 
| 488 |  | -					return true; | 
| 489 |  | -				} | 
| 490 |  | - | 
| 491 |  | -				if (it instanceof WildcardType wt) { | 
| 492 |  | -					return isClassBounded(wt.getLowerBounds()) && isClassBounded(wt.getUpperBounds()); | 
| 493 |  | -				} | 
| 494 |  | - | 
| 495 |  | -				return false; | 
| 496 |  | -			}); | 
| 497 |  | -		} | 
| 498 |  | - | 
| 499 |  | -		private boolean isUnwanted(ResolvableType resolvableType) { | 
| 500 |  | - | 
| 501 |  | -			return testGenericType(resolvableType, o -> { | 
| 502 |  | - | 
| 503 |  | -				if (o instanceof WildcardType wt) { | 
| 504 |  | -					return !isResolvable(wt.getLowerBounds()) || !isResolvable(wt.getUpperBounds()); | 
| 505 |  | -				} | 
| 506 |  | - | 
| 507 |  | -				return unwantedMethodVariables.contains(o); | 
| 508 |  | -			}); | 
| 509 |  | -		} | 
| 510 |  | - | 
| 511 |  | -		private static boolean testGenericType(ResolvableType resolvableType, Predicate<Type> predicate) { | 
| 512 |  | - | 
| 513 |  | -			if (predicate.test(resolvableType.getType())) { | 
| 514 |  | -				return true; | 
| 515 |  | -			} | 
| 516 |  | - | 
| 517 |  | -			ResolvableType[] generics = resolvableType.getGenerics(); | 
| 518 |  | -			for (ResolvableType generic : generics) { | 
| 519 |  | -				if (testGenericType(generic, predicate)) { | 
| 520 |  | -					return true; | 
| 521 |  | -				} | 
| 522 |  | -			} | 
| 523 |  | - | 
| 524 |  | -			return false; | 
| 525 |  | -		} | 
| 526 |  | - | 
| 527 |  | -		private static boolean isClassBounded(Type[] bounds) { | 
| 528 |  | - | 
| 529 |  | -			for (Type bound : bounds) { | 
| 530 |  | - | 
| 531 |  | -				if (isClass(bound)) { | 
| 532 |  | -					continue; | 
| 533 |  | -				} | 
| 534 |  | - | 
| 535 |  | -				return false; | 
| 536 |  | -			} | 
| 537 |  | - | 
| 538 |  | -			return true; | 
| 539 |  | -		} | 
| 540 |  | - | 
| 541 |  | -		private static boolean isClass(Type type) { | 
| 542 |  | -			return type instanceof Class; | 
| 543 |  | -		} | 
| 544 |  | - | 
| 545 |  | -	} | 
| 546 |  | - | 
| 547 | 334 | 	/** | 
| 548 | 335 | 	 * Factory interface to conditionally create {@link MethodContributor} instances. An implementation may decide whether | 
| 549 | 336 | 	 * to return a {@link MethodContributor} or {@literal null}, if no method (code or metadata) should be contributed. | 
|  | 
0 commit comments