Compare commits
	
		
			10 Commits
		
	
	
		
			03d1adf981
			...
			b64126a01b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | b64126a01b | ||
|   | 8169fd6dbd | ||
|   | 632e6f2dcd | ||
|   | 18d4917098 | ||
|   | 955e6aa8b2 | ||
|   | 956cfd0811 | ||
|   | 4dcab43b6a | ||
|   | ebe778fbaf | ||
|   | 11312c2807 | ||
|   | 6ea9553e63 | 
							
								
								
									
										12
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | # | ||||||
|  | # https://help.github.com/articles/dealing-with-line-endings/ | ||||||
|  | # | ||||||
|  | # Linux start script should use lf | ||||||
|  | /gradlew        text eol=lf | ||||||
|  | 
 | ||||||
|  | # These are Windows script files and should use crlf | ||||||
|  | *.bat           text eol=crlf | ||||||
|  | 
 | ||||||
|  | # Binary files should be left untouched | ||||||
|  | *.jar           binary | ||||||
|  | 
 | ||||||
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | # Ignore Gradle project-specific cache directory | ||||||
|  | .gradle | ||||||
|  | 
 | ||||||
|  | # Ignore Gradle build output directory | ||||||
|  | build | ||||||
|  | 
 | ||||||
|  | # Custom | ||||||
|  | .idea | ||||||
							
								
								
									
										67
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								build.gradle
									
									
									
									
									
								
							| @ -1,10 +1,73 @@ | |||||||
| plugins { | plugins { | ||||||
|     id 'GroowtConventions' |  | ||||||
|     id 'java-library' |     id 'java-library' | ||||||
|  |     id 'maven-publish' | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | group = 'com.jessebrault.di' | ||||||
|  | version = '0.1.0' | ||||||
|  | 
 | ||||||
|  | repositories { | ||||||
|  |     mavenCentral() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| dependencies { | dependencies { | ||||||
|     api libs.jakarta.inject |     api libs.jakarta.inject | ||||||
|  |     api libs.groovy | ||||||
|     compileOnlyApi libs.jetbrains.anotations |     compileOnlyApi libs.jetbrains.anotations | ||||||
|     implementation libs.slf4j.api, libs.groovy |     implementation libs.slf4j.api | ||||||
|  | 
 | ||||||
|  |     testImplementation libs.junit.jupiter.api | ||||||
|  |     testRuntimeOnly libs.log4j.core, libs.log4j.slf4jBinding | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test { | ||||||
|  |     testLogging.showStandardStreams = true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | testing { | ||||||
|  |     suites { | ||||||
|  |         test { | ||||||
|  |             useJUnitJupiter() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | java { | ||||||
|  |     withSourcesJar() | ||||||
|  |     toolchain { | ||||||
|  |         languageVersion = JavaLanguageVersion.of(21) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | jar { | ||||||
|  |     archiveBaseName = 'di' | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sourcesJar { | ||||||
|  |     archiveBaseName = 'di' | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | publishing { | ||||||
|  |     repositories { | ||||||
|  |         maven { | ||||||
|  |             name = "Gitea" | ||||||
|  |             url = uri("https://git.jessebrault.com/api/packages/jessebrault/maven") | ||||||
|  | 
 | ||||||
|  |             credentials(HttpHeaderCredentials) { | ||||||
|  |                 name = "Authorization" | ||||||
|  |                 value = "token ${System.getenv("GITEA_ACCESS_TOKEN")}" | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             authentication { | ||||||
|  |                 header(HttpHeaderAuthentication) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     publications { | ||||||
|  |         create('di', MavenPublication) { | ||||||
|  |             artifactId = 'di' | ||||||
|  |             from components.java | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								gradle.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								gradle.properties
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | # This file was generated by the Gradle 'init' task. | ||||||
|  | # https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties | ||||||
|  | 
 | ||||||
|  | org.gradle.configuration-cache=true | ||||||
|  | org.gradle.parallel=true | ||||||
|  | org.gradle.caching=true | ||||||
|  | 
 | ||||||
							
								
								
									
										19
									
								
								gradle/libs.versions.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								gradle/libs.versions.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | # This file was generated by the Gradle 'init' task. | ||||||
|  | # https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format | ||||||
|  | 
 | ||||||
|  | [versions] | ||||||
|  | groovy = '4.0.27' | ||||||
|  | jakarta-inject = '2.0.1' | ||||||
|  | jetbrains-annotations = '26.0.2' | ||||||
|  | junit = '5.13.0' | ||||||
|  | log4j = '2.24.3' | ||||||
|  | slf4j = '2.0.17' | ||||||
|  | 
 | ||||||
|  | [libraries] | ||||||
|  | groovy = { module = 'org.apache.groovy:groovy', version.ref = 'groovy' } | ||||||
|  | jakarta-inject = { module = 'jakarta.inject:jakarta.inject-api', version.ref = 'jakarta-inject' } | ||||||
|  | jetbrains-anotations = { module = 'org.jetbrains:annotations', version.ref = 'jetbrains-annotations' } | ||||||
|  | slf4j-api = { module = 'org.slf4j:slf4j-api', version.ref = 'slf4j' } | ||||||
|  | junit-jupiter-api = { module = 'org.junit.jupiter:junit-jupiter-api', version.ref = 'junit' } | ||||||
|  | log4j-core = { module = 'org.apache.logging.log4j:log4j-core', version.ref = 'log4j' } | ||||||
|  | log4j-slf4jBinding = { module = 'org.apache.logging.log4j:log4j-slf4j2-impl', version.ref = 'log4j' } | ||||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										7
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | distributionBase=GRADLE_USER_HOME | ||||||
|  | distributionPath=wrapper/dists | ||||||
|  | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip | ||||||
|  | networkTimeout=10000 | ||||||
|  | validateDistributionUrl=true | ||||||
|  | zipStoreBase=GRADLE_USER_HOME | ||||||
|  | zipStorePath=wrapper/dists | ||||||
							
								
								
									
										251
									
								
								gradlew
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										251
									
								
								gradlew
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @ -0,0 +1,251 @@ | |||||||
|  | #!/bin/sh | ||||||
|  | 
 | ||||||
|  | # | ||||||
|  | # Copyright © 2015-2021 the 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. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: Apache-2.0 | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | ############################################################################## | ||||||
|  | # | ||||||
|  | #   Gradle start up script for POSIX generated by Gradle. | ||||||
|  | # | ||||||
|  | #   Important for running: | ||||||
|  | # | ||||||
|  | #   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is | ||||||
|  | #       noncompliant, but you have some other compliant shell such as ksh or | ||||||
|  | #       bash, then to run this script, type that shell name before the whole | ||||||
|  | #       command line, like: | ||||||
|  | # | ||||||
|  | #           ksh Gradle | ||||||
|  | # | ||||||
|  | #       Busybox and similar reduced shells will NOT work, because this script | ||||||
|  | #       requires all of these POSIX shell features: | ||||||
|  | #         * functions; | ||||||
|  | #         * expansions «$var», «${var}», «${var:-default}», «${var+SET}», | ||||||
|  | #           «${var#prefix}», «${var%suffix}», and «$( cmd )»; | ||||||
|  | #         * compound commands having a testable exit status, especially «case»; | ||||||
|  | #         * various built-in commands including «command», «set», and «ulimit». | ||||||
|  | # | ||||||
|  | #   Important for patching: | ||||||
|  | # | ||||||
|  | #   (2) This script targets any POSIX shell, so it avoids extensions provided | ||||||
|  | #       by Bash, Ksh, etc; in particular arrays are avoided. | ||||||
|  | # | ||||||
|  | #       The "traditional" practice of packing multiple parameters into a | ||||||
|  | #       space-separated string is a well documented source of bugs and security | ||||||
|  | #       problems, so this is (mostly) avoided, by progressively accumulating | ||||||
|  | #       options in "$@", and eventually passing that to Java. | ||||||
|  | # | ||||||
|  | #       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, | ||||||
|  | #       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; | ||||||
|  | #       see the in-line comments for details. | ||||||
|  | # | ||||||
|  | #       There are tweaks for specific operating systems such as AIX, CygWin, | ||||||
|  | #       Darwin, MinGW, and NonStop. | ||||||
|  | # | ||||||
|  | #   (3) This script is generated from the Groovy template | ||||||
|  | #       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt | ||||||
|  | #       within the Gradle project. | ||||||
|  | # | ||||||
|  | #       You can find Gradle at https://github.com/gradle/gradle/. | ||||||
|  | # | ||||||
|  | ############################################################################## | ||||||
|  | 
 | ||||||
|  | # Attempt to set APP_HOME | ||||||
|  | 
 | ||||||
|  | # Resolve links: $0 may be a link | ||||||
|  | app_path=$0 | ||||||
|  | 
 | ||||||
|  | # Need this for daisy-chained symlinks. | ||||||
|  | while | ||||||
|  |     APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path | ||||||
|  |     [ -h "$app_path" ] | ||||||
|  | do | ||||||
|  |     ls=$( ls -ld "$app_path" ) | ||||||
|  |     link=${ls#*' -> '} | ||||||
|  |     case $link in             #( | ||||||
|  |       /*)   app_path=$link ;; #( | ||||||
|  |       *)    app_path=$APP_HOME$link ;; | ||||||
|  |     esac | ||||||
|  | done | ||||||
|  | 
 | ||||||
|  | # This is normally unused | ||||||
|  | # shellcheck disable=SC2034 | ||||||
|  | APP_BASE_NAME=${0##*/} | ||||||
|  | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) | ||||||
|  | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit | ||||||
|  | 
 | ||||||
|  | # Use the maximum available, or set MAX_FD != -1 to use that value. | ||||||
|  | MAX_FD=maximum | ||||||
|  | 
 | ||||||
|  | warn () { | ||||||
|  |     echo "$*" | ||||||
|  | } >&2 | ||||||
|  | 
 | ||||||
|  | die () { | ||||||
|  |     echo | ||||||
|  |     echo "$*" | ||||||
|  |     echo | ||||||
|  |     exit 1 | ||||||
|  | } >&2 | ||||||
|  | 
 | ||||||
|  | # OS specific support (must be 'true' or 'false'). | ||||||
|  | cygwin=false | ||||||
|  | msys=false | ||||||
|  | darwin=false | ||||||
|  | nonstop=false | ||||||
|  | case "$( uname )" in                #( | ||||||
|  |   CYGWIN* )         cygwin=true  ;; #( | ||||||
|  |   Darwin* )         darwin=true  ;; #( | ||||||
|  |   MSYS* | MINGW* )  msys=true    ;; #( | ||||||
|  |   NONSTOP* )        nonstop=true ;; | ||||||
|  | esac | ||||||
|  | 
 | ||||||
|  | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Determine the Java command to use to start the JVM. | ||||||
|  | if [ -n "$JAVA_HOME" ] ; then | ||||||
|  |     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then | ||||||
|  |         # IBM's JDK on AIX uses strange locations for the executables | ||||||
|  |         JAVACMD=$JAVA_HOME/jre/sh/java | ||||||
|  |     else | ||||||
|  |         JAVACMD=$JAVA_HOME/bin/java | ||||||
|  |     fi | ||||||
|  |     if [ ! -x "$JAVACMD" ] ; then | ||||||
|  |         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME | ||||||
|  | 
 | ||||||
|  | Please set the JAVA_HOME variable in your environment to match the | ||||||
|  | location of your Java installation." | ||||||
|  |     fi | ||||||
|  | else | ||||||
|  |     JAVACMD=java | ||||||
|  |     if ! command -v java >/dev/null 2>&1 | ||||||
|  |     then | ||||||
|  |         die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | ||||||
|  | 
 | ||||||
|  | Please set the JAVA_HOME variable in your environment to match the | ||||||
|  | location of your Java installation." | ||||||
|  |     fi | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | # Increase the maximum file descriptors if we can. | ||||||
|  | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then | ||||||
|  |     case $MAX_FD in #( | ||||||
|  |       max*) | ||||||
|  |         # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. | ||||||
|  |         # shellcheck disable=SC2039,SC3045 | ||||||
|  |         MAX_FD=$( ulimit -H -n ) || | ||||||
|  |             warn "Could not query maximum file descriptor limit" | ||||||
|  |     esac | ||||||
|  |     case $MAX_FD in  #( | ||||||
|  |       '' | soft) :;; #( | ||||||
|  |       *) | ||||||
|  |         # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. | ||||||
|  |         # shellcheck disable=SC2039,SC3045 | ||||||
|  |         ulimit -n "$MAX_FD" || | ||||||
|  |             warn "Could not set maximum file descriptor limit to $MAX_FD" | ||||||
|  |     esac | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | # Collect all arguments for the java command, stacking in reverse order: | ||||||
|  | #   * args from the command line | ||||||
|  | #   * the main class name | ||||||
|  | #   * -classpath | ||||||
|  | #   * -D...appname settings | ||||||
|  | #   * --module-path (only if needed) | ||||||
|  | #   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. | ||||||
|  | 
 | ||||||
|  | # For Cygwin or MSYS, switch paths to Windows format before running java | ||||||
|  | if "$cygwin" || "$msys" ; then | ||||||
|  |     APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) | ||||||
|  |     CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) | ||||||
|  | 
 | ||||||
|  |     JAVACMD=$( cygpath --unix "$JAVACMD" ) | ||||||
|  | 
 | ||||||
|  |     # Now convert the arguments - kludge to limit ourselves to /bin/sh | ||||||
|  |     for arg do | ||||||
|  |         if | ||||||
|  |             case $arg in                                #( | ||||||
|  |               -*)   false ;;                            # don't mess with options #( | ||||||
|  |               /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath | ||||||
|  |                     [ -e "$t" ] ;;                      #( | ||||||
|  |               *)    false ;; | ||||||
|  |             esac | ||||||
|  |         then | ||||||
|  |             arg=$( cygpath --path --ignore --mixed "$arg" ) | ||||||
|  |         fi | ||||||
|  |         # Roll the args list around exactly as many times as the number of | ||||||
|  |         # args, so each arg winds up back in the position where it started, but | ||||||
|  |         # possibly modified. | ||||||
|  |         # | ||||||
|  |         # NB: a `for` loop captures its iteration list before it begins, so | ||||||
|  |         # changing the positional parameters here affects neither the number of | ||||||
|  |         # iterations, nor the values presented in `arg`. | ||||||
|  |         shift                   # remove old arg | ||||||
|  |         set -- "$@" "$arg"      # push replacement arg | ||||||
|  |     done | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||||
|  | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | ||||||
|  | 
 | ||||||
|  | # Collect all arguments for the java command: | ||||||
|  | #   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, | ||||||
|  | #     and any embedded shellness will be escaped. | ||||||
|  | #   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be | ||||||
|  | #     treated as '${Hostname}' itself on the command line. | ||||||
|  | 
 | ||||||
|  | set -- \ | ||||||
|  |         "-Dorg.gradle.appname=$APP_BASE_NAME" \ | ||||||
|  |         -classpath "$CLASSPATH" \ | ||||||
|  |         org.gradle.wrapper.GradleWrapperMain \ | ||||||
|  |         "$@" | ||||||
|  | 
 | ||||||
|  | # Stop when "xargs" is not available. | ||||||
|  | if ! command -v xargs >/dev/null 2>&1 | ||||||
|  | then | ||||||
|  |     die "xargs is not available" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | # Use "xargs" to parse quoted args. | ||||||
|  | # | ||||||
|  | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. | ||||||
|  | # | ||||||
|  | # In Bash we could simply go: | ||||||
|  | # | ||||||
|  | #   readarray ARGS < <( xargs -n1 <<<"$var" ) && | ||||||
|  | #   set -- "${ARGS[@]}" "$@" | ||||||
|  | # | ||||||
|  | # but POSIX shell has neither arrays nor command substitution, so instead we | ||||||
|  | # post-process each arg (as a line of input to sed) to backslash-escape any | ||||||
|  | # character that might be a shell metacharacter, then use eval to reverse | ||||||
|  | # that process (while maintaining the separation between arguments), and wrap | ||||||
|  | # the whole thing up as a single "set" statement. | ||||||
|  | # | ||||||
|  | # This will of course break if any of these variables contains a newline or | ||||||
|  | # an unmatched quote. | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | eval "set -- $( | ||||||
|  |         printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | | ||||||
|  |         xargs -n1 | | ||||||
|  |         sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | | ||||||
|  |         tr '\n' ' ' | ||||||
|  |     )" '"$@"' | ||||||
|  | 
 | ||||||
|  | exec "$JAVACMD" "$@" | ||||||
							
								
								
									
										94
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | |||||||
|  | @rem | ||||||
|  | @rem Copyright 2015 the original author or authors. | ||||||
|  | @rem | ||||||
|  | @rem Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | @rem you may not use this file except in compliance with the License. | ||||||
|  | @rem You may obtain a copy of the License at | ||||||
|  | @rem | ||||||
|  | @rem      https://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | @rem | ||||||
|  | @rem Unless required by applicable law or agreed to in writing, software | ||||||
|  | @rem distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | @rem See the License for the specific language governing permissions and | ||||||
|  | @rem limitations under the License. | ||||||
|  | @rem | ||||||
|  | @rem SPDX-License-Identifier: Apache-2.0 | ||||||
|  | @rem | ||||||
|  | 
 | ||||||
|  | @if "%DEBUG%"=="" @echo off | ||||||
|  | @rem ########################################################################## | ||||||
|  | @rem | ||||||
|  | @rem  Gradle startup script for Windows | ||||||
|  | @rem | ||||||
|  | @rem ########################################################################## | ||||||
|  | 
 | ||||||
|  | @rem Set local scope for the variables with windows NT shell | ||||||
|  | if "%OS%"=="Windows_NT" setlocal | ||||||
|  | 
 | ||||||
|  | set DIRNAME=%~dp0 | ||||||
|  | if "%DIRNAME%"=="" set DIRNAME=. | ||||||
|  | @rem This is normally unused | ||||||
|  | set APP_BASE_NAME=%~n0 | ||||||
|  | set APP_HOME=%DIRNAME% | ||||||
|  | 
 | ||||||
|  | @rem Resolve any "." and ".." in APP_HOME to make it shorter. | ||||||
|  | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi | ||||||
|  | 
 | ||||||
|  | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||||
|  | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" | ||||||
|  | 
 | ||||||
|  | @rem Find java.exe | ||||||
|  | if defined JAVA_HOME goto findJavaFromJavaHome | ||||||
|  | 
 | ||||||
|  | set JAVA_EXE=java.exe | ||||||
|  | %JAVA_EXE% -version >NUL 2>&1 | ||||||
|  | if %ERRORLEVEL% equ 0 goto execute | ||||||
|  | 
 | ||||||
|  | echo. 1>&2 | ||||||
|  | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 | ||||||
|  | echo. 1>&2 | ||||||
|  | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 | ||||||
|  | echo location of your Java installation. 1>&2 | ||||||
|  | 
 | ||||||
|  | goto fail | ||||||
|  | 
 | ||||||
|  | :findJavaFromJavaHome | ||||||
|  | set JAVA_HOME=%JAVA_HOME:"=% | ||||||
|  | set JAVA_EXE=%JAVA_HOME%/bin/java.exe | ||||||
|  | 
 | ||||||
|  | if exist "%JAVA_EXE%" goto execute | ||||||
|  | 
 | ||||||
|  | echo. 1>&2 | ||||||
|  | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 | ||||||
|  | echo. 1>&2 | ||||||
|  | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 | ||||||
|  | echo location of your Java installation. 1>&2 | ||||||
|  | 
 | ||||||
|  | goto fail | ||||||
|  | 
 | ||||||
|  | :execute | ||||||
|  | @rem Setup the command line | ||||||
|  | 
 | ||||||
|  | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @rem Execute Gradle | ||||||
|  | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* | ||||||
|  | 
 | ||||||
|  | :end | ||||||
|  | @rem End local scope for the variables with windows NT shell | ||||||
|  | if %ERRORLEVEL% equ 0 goto mainEnd | ||||||
|  | 
 | ||||||
|  | :fail | ||||||
|  | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of | ||||||
|  | rem the _cmd.exe /c_ return code! | ||||||
|  | set EXIT_CODE=%ERRORLEVEL% | ||||||
|  | if %EXIT_CODE% equ 0 set EXIT_CODE=1 | ||||||
|  | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% | ||||||
|  | exit /b %EXIT_CODE% | ||||||
|  | 
 | ||||||
|  | :mainEnd | ||||||
|  | if "%OS%"=="Windows_NT" endlocal | ||||||
|  | 
 | ||||||
|  | :omega | ||||||
							
								
								
									
										1
									
								
								settings.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								settings.gradle
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | rootProject.name = 'di' | ||||||
| @ -5,6 +5,8 @@ import org.jetbrains.annotations.Nullable; | |||||||
| 
 | 
 | ||||||
| import java.lang.reflect.*; | import java.lang.reflect.*; | ||||||
| import java.util.*; | import java.util.*; | ||||||
|  | import java.util.function.Consumer; | ||||||
|  | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| import static groowt.util.di.ObjectFactoryUtil.toTypes; | import static groowt.util.di.ObjectFactoryUtil.toTypes; | ||||||
| 
 | 
 | ||||||
| @ -19,6 +21,89 @@ public abstract class AbstractInjectingObjectFactory implements ObjectFactory { | |||||||
|             Class<?>[] paramTypes |             Class<?>[] paramTypes | ||||||
|     ) {} |     ) {} | ||||||
| 
 | 
 | ||||||
|  |     protected record Resolved(Class<?> type, Object object) {} | ||||||
|  | 
 | ||||||
|  |     private static final class DeferredSetters { | ||||||
|  | 
 | ||||||
|  |         private final Class<?> forType; | ||||||
|  |         private final List<Consumer<Object>> actions = new ArrayList<>(); | ||||||
|  | 
 | ||||||
|  |         public DeferredSetters(Class<?> forType) { | ||||||
|  |             this.forType = forType; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Class<?> getForType() { | ||||||
|  |             return this.forType; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public List<Consumer<Object>> getActions() { | ||||||
|  |             return this.actions; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected static class CreateContext { | ||||||
|  | 
 | ||||||
|  |         private final Deque<Class<?>> constructionStack = new LinkedList<>(); | ||||||
|  |         private final Deque<DeferredSetters> deferredSettersStack = new LinkedList<>(); | ||||||
|  |         private final List<Resolved> allResolved = new ArrayList<>(); | ||||||
|  | 
 | ||||||
|  |         public CreateContext(Class<?> targetType) { | ||||||
|  |             this.pushConstruction(targetType); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void checkForCircularDependency(Class<?> typeToConstruct) { | ||||||
|  |             if (constructionStack.contains(typeToConstruct)) { | ||||||
|  |                 throw new IllegalStateException( | ||||||
|  |                         "Detected a circular constructor dependency for " + typeToConstruct.getName() | ||||||
|  |                                 + " . Please use setter methods instead. Current construction stack: " | ||||||
|  |                                 + constructionStack.stream() | ||||||
|  |                                         .map(Class::getName) | ||||||
|  |                                         .collect(Collectors.joining(", ")) | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void pushConstruction(Class<?> type) { | ||||||
|  |             this.constructionStack.push(type); | ||||||
|  |             this.deferredSettersStack.push(new DeferredSetters(type)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void popConstruction() { | ||||||
|  |             this.constructionStack.pop(); | ||||||
|  |             this.deferredSettersStack.pop(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public boolean containsConstruction(Class<?> type) { | ||||||
|  |             return this.constructionStack.contains(type); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void addDeferredSetterAction(Class<?> forType, Consumer<Object> action) { | ||||||
|  |             boolean found = false; | ||||||
|  |             for (final DeferredSetters deferredSetters : this.deferredSettersStack) { | ||||||
|  |                 if (deferredSetters.getForType().equals(forType)) { | ||||||
|  |                     found = true; | ||||||
|  |                     deferredSetters.getActions().add(action); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (!found) { | ||||||
|  |                 throw new IllegalArgumentException( | ||||||
|  |                         "There is no construction for type " + forType.getName() + " which should be deferred." | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public List<Consumer<Object>> getDeferredSetterActions() { | ||||||
|  |             return this.deferredSettersStack.getFirst().getActions(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public List<Resolved> getAllResolved() { | ||||||
|  |             return this.allResolved; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private final Map<Class<?>, Constructor<?>[]> cachedAllConstructors = new HashMap<>(); |     private final Map<Class<?>, Constructor<?>[]> cachedAllConstructors = new HashMap<>(); | ||||||
|     private final Collection<CachedInjectConstructor<?>> cachedInjectConstructors = new ArrayList<>(); |     private final Collection<CachedInjectConstructor<?>> cachedInjectConstructors = new ArrayList<>(); | ||||||
|     private final Collection<CachedNonInjectConstructor<?>> cachedNonInjectConstructors = new ArrayList<>(); |     private final Collection<CachedNonInjectConstructor<?>> cachedNonInjectConstructors = new ArrayList<>(); | ||||||
| @ -81,14 +166,27 @@ public abstract class AbstractInjectingObjectFactory implements ObjectFactory { | |||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private static boolean areArgsAssignable(Class<?>[] paramTypes, Object[] givenArgs) { | ||||||
|  |         if (paramTypes.length != givenArgs.length) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         for (int i = 0; i < paramTypes.length; i++) { | ||||||
|  |             if (!paramTypes[i].isInstance(givenArgs[i])) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * @implNote If overridden, please cache any found non-inject constructors using {@link #putCachedNonInjectConstructor}. |      * @implNote If overridden, please cache any found non-inject constructors using | ||||||
|  |      *   {@link #putCachedNonInjectConstructor}. | ||||||
|      * |      * | ||||||
|      * @param clazz the {@link Class} in which to search for a constructor which does not have an <code>{@literal @}Inject</code> |      * @param clazz the {@link Class} in which to search for a constructor which does | ||||||
|      *              annotation |      *              not have an <code>{@literal @}Inject</code> annotation. | ||||||
|      * @param constructorArgs the given constructor args |      * @param constructorArgs the given constructor args | ||||||
|      * @return the found non-inject constructor appropriate for the given constructor args, or {@code null} if no |      * @return the found non-inject constructor appropriate for the given constructor args, | ||||||
|      * such constructor exists |      * or {@code null} if no such constructor exists | ||||||
|      * @param <T> the type |      * @param <T> the type | ||||||
|      */ |      */ | ||||||
|     @SuppressWarnings("unchecked") |     @SuppressWarnings("unchecked") | ||||||
| @ -101,7 +199,7 @@ public abstract class AbstractInjectingObjectFactory implements ObjectFactory { | |||||||
| 
 | 
 | ||||||
|         final Constructor<?>[] constructors = this.cachedAllConstructors.computeIfAbsent(clazz, Class::getConstructors); |         final Constructor<?>[] constructors = this.cachedAllConstructors.computeIfAbsent(clazz, Class::getConstructors); | ||||||
|         for (Constructor<?> constructor : constructors) { |         for (Constructor<?> constructor : constructors) { | ||||||
|             if (Arrays.equals(constructor.getParameterTypes(), types)) { |             if (areArgsAssignable(constructor.getParameterTypes(), constructorArgs)) { | ||||||
|                 final Constructor<T> found = (Constructor<T>) constructor; |                 final Constructor<T> found = (Constructor<T>) constructor; | ||||||
|                 this.putCachedNonInjectConstructor(new CachedNonInjectConstructor<>(clazz, found, types)); |                 this.putCachedNonInjectConstructor(new CachedNonInjectConstructor<>(clazz, found, types)); | ||||||
|                 return found; |                 return found; | ||||||
| @ -156,42 +254,104 @@ public abstract class AbstractInjectingObjectFactory implements ObjectFactory { | |||||||
|     protected Parameter getCachedInjectParameter(Method setter) { |     protected Parameter getCachedInjectParameter(Method setter) { | ||||||
|         return this.cachedSetterParameters.computeIfAbsent(setter, s -> { |         return this.cachedSetterParameters.computeIfAbsent(setter, s -> { | ||||||
|             if (s.getParameterCount() != 1) { |             if (s.getParameterCount() != 1) { | ||||||
|                 throw new IllegalArgumentException("Setter " + s.getName() + " has a parameter count other than one (1)!"); |                 throw new IllegalArgumentException( | ||||||
|  |                         "Setter " + s.getName() + " has a parameter count other than one (1)!" | ||||||
|  |                 ); | ||||||
|             } |             } | ||||||
|             return s.getParameters()[0]; |             return s.getParameters()[0]; | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected void injectSetter(Object target, Method setter) { |     private @Nullable Object findInContext(CreateContext context, Class<?> type) { | ||||||
|         try { |         for (final Resolved resolved : context.getAllResolved()) { | ||||||
|             setter.invoke(target, this.getSetterInjectArg(target.getClass(), setter, this.getCachedInjectParameter(setter))); |             if (type.isAssignableFrom(resolved.type)) { | ||||||
|         } catch (InvocationTargetException | IllegalAccessException e) { |                 return resolved.object; | ||||||
|             throw new RuntimeException(e); |             } | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected void injectSetter(CreateContext context, Object target, Method setter) { | ||||||
|  |         final Parameter injectParam = this.getCachedInjectParameter(setter); | ||||||
|  |         final Class<?> typeToInject = injectParam.getType(); | ||||||
|  |         final @Nullable Object fromContext = this.findInContext(context, typeToInject); | ||||||
|  | 
 | ||||||
|  |         final Consumer<Object> setterAction = arg -> { | ||||||
|  |             try { | ||||||
|  |                 setter.invoke(target, arg); | ||||||
|  |             } catch (InvocationTargetException | IllegalAccessException e) { | ||||||
|  |                 throw new RuntimeException(e); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         if (context.containsConstruction(typeToInject)) { | ||||||
|  |             context.addDeferredSetterAction(typeToInject, setterAction); | ||||||
|  |         } else { | ||||||
|  |             final Object arg; | ||||||
|  |             if (fromContext != null) { | ||||||
|  |                 arg = fromContext; | ||||||
|  |             } else { | ||||||
|  |                 arg = this.getSetterInjectArg(context, target.getClass(), setter, injectParam); | ||||||
|  |                 context.getAllResolved().add(new Resolved(typeToInject, arg)); | ||||||
|  |             } | ||||||
|  |             setterAction.accept(arg); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected void injectSetters(Object target) { |     protected void injectSetters(CreateContext context, Object target) { | ||||||
|         this.getCachedSettersFor(target).forEach(setter -> this.injectSetter(target, setter)); |         this.getCachedSettersFor(target).forEach(setter -> this.injectSetter(context, target, setter)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * {@inheritDoc} |      * {@inheritDoc} | ||||||
|      */ |      */ | ||||||
|     @Override |     @Override | ||||||
|     public <T> T createInstance(Class<T> clazz, Object... constructorArgs) { |     public <T> T createInstance(Class<T> type, Object... constructorArgs) { | ||||||
|         final Constructor<T> constructor = this.findConstructor(clazz, constructorArgs); |         final Constructor<T> constructor = this.findConstructor(type, constructorArgs); | ||||||
|         final Object[] allArgs = this.createArgs(constructor, constructorArgs); |         final CreateContext context = new CreateContext(type); | ||||||
|  |         final Object[] allArgs = this.createArgs(context, constructor, constructorArgs); | ||||||
|         try { |         try { | ||||||
|             final T instance = constructor.newInstance(allArgs); |             final T instance = constructor.newInstance(allArgs); | ||||||
|             this.injectSetters(instance); |             context.getAllResolved().add(new Resolved(type, instance)); | ||||||
|  |             this.injectSetters(context, instance); | ||||||
|  |             final List<Consumer<Object>> deferredSetterActions = context.getDeferredSetterActions(); | ||||||
|  |             context.popConstruction(); | ||||||
|  |             deferredSetterActions.forEach(setterAction -> setterAction.accept(instance)); | ||||||
|             return instance; |             return instance; | ||||||
|         } catch (InvocationTargetException | IllegalAccessException | InstantiationException e) { |         } catch (InvocationTargetException | IllegalAccessException | InstantiationException e) { | ||||||
|             throw new RuntimeException(e); // In the future, we might have an option to ignore exceptions |             throw new RuntimeException(e); // In the future, we might have an option to ignore exceptions | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected abstract Object[] createArgs(Constructor<?> constructor, Object[] constructorArgs); |     protected <T> T createInstance(CreateContext context, Class<T> type, Object... givenArgs) { | ||||||
|  |         final Constructor<T> constructor = this.findConstructor(type, givenArgs); | ||||||
|  |         context.checkForCircularDependency(type); | ||||||
|  |         context.pushConstruction(type); | ||||||
|  |         final Object[] allArgs = this.createArgs(context, constructor, givenArgs); | ||||||
|  |         try { | ||||||
|  |             final T instance = constructor.newInstance(allArgs); | ||||||
|  |             context.getAllResolved().add(new Resolved(type, instance)); | ||||||
|  |             this.injectSetters(context, instance); | ||||||
|  |             final List<Consumer<Object>> deferredSetterActions = context.getDeferredSetterActions(); | ||||||
|  |             context.popConstruction(); | ||||||
|  |             deferredSetterActions.forEach(setterAction -> setterAction.accept(instance)); | ||||||
|  |             return instance; | ||||||
|  |         } catch (InvocationTargetException | IllegalAccessException | InstantiationException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     protected abstract Object getSetterInjectArg(Class<?> targetType, Method setter, Parameter toInject); |     protected abstract Object[] createArgs( | ||||||
|  |             CreateContext context, | ||||||
|  |             Constructor<?> constructor, | ||||||
|  |             Object[] constructorArgs | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     protected abstract Object getSetterInjectArg( | ||||||
|  |             CreateContext context, | ||||||
|  |             Class<?> targetType, | ||||||
|  |             Method setter, | ||||||
|  |             Parameter toInject | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -14,7 +14,8 @@ import java.util.function.Supplier; | |||||||
| 
 | 
 | ||||||
| import static groowt.util.di.RegistryObjectFactoryUtil.orElseSupply; | import static groowt.util.di.RegistryObjectFactoryUtil.orElseSupply; | ||||||
| 
 | 
 | ||||||
| public abstract class AbstractRegistryObjectFactory extends AbstractInjectingObjectFactory implements RegistryObjectFactory { | public abstract class AbstractRegistryObjectFactory extends AbstractInjectingObjectFactory | ||||||
|  |         implements RegistryObjectFactory { | ||||||
| 
 | 
 | ||||||
|     public static abstract class AbstractBuilder<T extends DefaultRegistryObjectFactory> implements Builder<T> { |     public static abstract class AbstractBuilder<T extends DefaultRegistryObjectFactory> implements Builder<T> { | ||||||
| 
 | 
 | ||||||
| @ -75,8 +76,8 @@ public abstract class AbstractRegistryObjectFactory extends AbstractInjectingObj | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void configureRegistry(Consumer<? super Registry> use) { |     public Registry getRegistry() { | ||||||
|         use.accept(this.registry); |         return this.registry; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  | |||||||
| @ -27,6 +27,10 @@ public final class BindingUtil { | |||||||
|         return bc -> {}; |         return bc -> {}; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static <T> KeyHolder<NamedRegistryExtension, String, T> named(String name, Class<T> type) { | ||||||
|  |         return new SimpleKeyHolder<>(NamedRegistryExtension.class, type, name); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private BindingUtil() {} |     private BindingUtil() {} | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,31 +3,58 @@ package groowt.util.di; | |||||||
| import org.jetbrains.annotations.Nullable; | import org.jetbrains.annotations.Nullable; | ||||||
| 
 | 
 | ||||||
| import java.lang.annotation.Annotation; | import java.lang.annotation.Annotation; | ||||||
| import java.util.ArrayList; | import java.util.*; | ||||||
| import java.util.Collection; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.function.Consumer; | import java.util.function.Consumer; | ||||||
| import java.util.function.Function; | import java.util.function.Function; | ||||||
| import java.util.function.Predicate; | import java.util.function.Predicate; | ||||||
| 
 | 
 | ||||||
| public class DefaultRegistry implements Registry { | public class DefaultRegistry implements Registry { | ||||||
| 
 | 
 | ||||||
|     protected record ClassKeyBinding<T>(Class<T> key, Binding<T> binding) {} |     protected static class BindingContainer { | ||||||
| 
 | 
 | ||||||
|     protected final Collection<ClassKeyBinding<?>> classBindings = new ArrayList<>(); |         private final Map<Class<?>, Binding<?>> bindings = new HashMap<>(); | ||||||
|  | 
 | ||||||
|  |         @SuppressWarnings("unchecked") | ||||||
|  |         public <T> @Nullable Binding<T> get(Class<T> key) { | ||||||
|  |             for (final var entry : bindings.entrySet()) { | ||||||
|  |                 if (entry.getKey().isAssignableFrom(key)) { | ||||||
|  |                     return (Binding<T>) entry.getValue(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public <T> void put(Class<T> key, Binding<T> binding) { | ||||||
|  |             this.bindings.put(key, binding); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void remove(Class<?> key) { | ||||||
|  |             this.bindings.remove(key); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public <T> void removeIf(Class<T> key, Predicate<? super Binding<T>> filter) { | ||||||
|  |             if (filter.test(this.get(key))) { | ||||||
|  |                 this.bindings.remove(key); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void clear() { | ||||||
|  |             this.bindings.clear(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected final BindingContainer bindingContainer = new BindingContainer(); | ||||||
|     protected final Collection<RegistryExtension> extensions = new ArrayList<>(); |     protected final Collection<RegistryExtension> extensions = new ArrayList<>(); | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void removeBinding(Class<?> key) { |     public void removeBinding(Class<?> key) { | ||||||
|         this.classBindings.removeIf(classKeyBinding -> classKeyBinding.key().equals(key)); |         this.bindingContainer.remove(key); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @SuppressWarnings("unchecked") |  | ||||||
|     @Override |     @Override | ||||||
|     public <T> void removeBindingIf(Class<T> key, Predicate<Binding<T>> filter) { |     public <T> void removeBindingIf(Class<T> key, Predicate<Binding<T>> filter) { | ||||||
|         this.classBindings.removeIf(classKeyBinding -> |         this.bindingContainer.removeIf(key, filter); | ||||||
|                 classKeyBinding.key().equals(key) && filter.test((Binding<T>) classKeyBinding.binding()) |  | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private <E extends RegistryExtension> List<E> getAllRegistryExtensions(Class<E> extensionType) { |     private <E extends RegistryExtension> List<E> getAllRegistryExtensions(Class<E> extensionType) { | ||||||
| @ -117,18 +144,12 @@ public class DefaultRegistry implements Registry { | |||||||
|     public <T> void bind(Class<T> key, Consumer<? super BindingConfigurator<T>> configure) { |     public <T> void bind(Class<T> key, Consumer<? super BindingConfigurator<T>> configure) { | ||||||
|         final var configurator = new SimpleBindingConfigurator<>(key); |         final var configurator = new SimpleBindingConfigurator<>(key); | ||||||
|         configure.accept(configurator); |         configure.accept(configurator); | ||||||
|         this.classBindings.add(new ClassKeyBinding<>(key, configurator.getBinding())); |         this.bindingContainer.put(key, configurator.getBinding()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @SuppressWarnings("unchecked") |  | ||||||
|     @Override |     @Override | ||||||
|     public @Nullable <T> Binding<T> getBinding(Class<T> key) { |     public <T> @Nullable Binding<T> getBinding(Class<T> key) { | ||||||
|         for (final var classKeyBinding : this.classBindings) { |         return this.bindingContainer.get(key); | ||||||
|             if (key.isAssignableFrom(classKeyBinding.key())) { |  | ||||||
|                 return (Binding<T>) classKeyBinding.binding(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private KeyBinder<?> findKeyBinder(Class<?> keyClass) { |     private KeyBinder<?> findKeyBinder(Class<?> keyClass) { | ||||||
| @ -189,7 +210,7 @@ public class DefaultRegistry implements Registry { | |||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void clearAllBindings() { |     public void clearAllBindings() { | ||||||
|         this.classBindings.clear(); |         this.bindingContainer.clear(); | ||||||
|         for (final var extension : this.extensions) { |         for (final var extension : this.extensions) { | ||||||
|             if (extension instanceof KeyBinder<?> keyBinder) { |             if (extension instanceof KeyBinder<?> keyBinder) { | ||||||
|                 keyBinder.clearAllBindings(); |                 keyBinder.clearAllBindings(); | ||||||
|  | |||||||
| @ -3,8 +3,6 @@ package groowt.util.di; | |||||||
| import groowt.util.di.filters.FilterHandler; | import groowt.util.di.filters.FilterHandler; | ||||||
| import groowt.util.di.filters.IterableFilterHandler; | import groowt.util.di.filters.IterableFilterHandler; | ||||||
| import org.jetbrains.annotations.Nullable; | import org.jetbrains.annotations.Nullable; | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
| 
 | 
 | ||||||
| import java.lang.annotation.Annotation; | import java.lang.annotation.Annotation; | ||||||
| import java.lang.reflect.Constructor; | import java.lang.reflect.Constructor; | ||||||
| @ -20,7 +18,8 @@ import static groowt.util.di.RegistryObjectFactoryUtil.*; | |||||||
| 
 | 
 | ||||||
| public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory { | public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory { | ||||||
| 
 | 
 | ||||||
|     public static final class Builder extends AbstractRegistryObjectFactory.AbstractBuilder<DefaultRegistryObjectFactory> { |     public static final class Builder | ||||||
|  |             extends AbstractRegistryObjectFactory.AbstractBuilder<DefaultRegistryObjectFactory> { | ||||||
| 
 | 
 | ||||||
|         /** |         /** | ||||||
|          * Creates a {@code Builder} initialized with a {@link DefaultRegistry}, which is in-turn configured with a |          * Creates a {@code Builder} initialized with a {@link DefaultRegistry}, which is in-turn configured with a | ||||||
| @ -74,7 +73,6 @@ public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; |     private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; | ||||||
|     private static final Logger logger = LoggerFactory.getLogger(DefaultRegistryObjectFactory.class); // leave it for the future! |  | ||||||
| 
 | 
 | ||||||
|     private final Collection<FilterHandler<?, ?>> filterHandlers; |     private final Collection<FilterHandler<?, ?>> filterHandlers; | ||||||
|     private final Collection<IterableFilterHandler<?, ?>> iterableFilterHandlers; |     private final Collection<IterableFilterHandler<?, ?>> iterableFilterHandlers; | ||||||
| @ -107,7 +105,9 @@ public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory | |||||||
|     @SuppressWarnings("unchecked") |     @SuppressWarnings("unchecked") | ||||||
|     protected final @Nullable Object tryQualifiers(Parameter parameter) { |     protected final @Nullable Object tryQualifiers(Parameter parameter) { | ||||||
|         final Class<?> paramType = parameter.getType(); |         final Class<?> paramType = parameter.getType(); | ||||||
|         final List<Annotation> qualifiers = RegistryObjectFactoryUtil.getQualifierAnnotations(parameter.getAnnotations()); |         final List<Annotation> qualifiers = RegistryObjectFactoryUtil.getQualifierAnnotations( | ||||||
|  |                 parameter.getAnnotations() | ||||||
|  |         ); | ||||||
|         if (qualifiers.size() > 1) { |         if (qualifiers.size() > 1) { | ||||||
|             throw new RuntimeException("Parameter " + parameter + " cannot have more than one Qualifier annotation."); |             throw new RuntimeException("Parameter " + parameter + " cannot have more than one Qualifier annotation."); | ||||||
|         } else if (qualifiers.size() == 1) { |         } else if (qualifiers.size() == 1) { | ||||||
| @ -115,7 +115,9 @@ public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory | |||||||
|             @SuppressWarnings("rawtypes") |             @SuppressWarnings("rawtypes") | ||||||
|             final QualifierHandler handler = this.getInSelfOrParent( |             final QualifierHandler handler = this.getInSelfOrParent( | ||||||
|                     f -> f.findQualifierHandler(qualifier.annotationType()), |                     f -> f.findQualifierHandler(qualifier.annotationType()), | ||||||
|                     () -> new RuntimeException("There is no configured QualifierHandler for " + qualifier.annotationType().getName()) |                     () -> new RuntimeException("There is no configured QualifierHandler for " | ||||||
|  |                             + qualifier.annotationType().getName() | ||||||
|  |                     ) | ||||||
|             ); |             ); | ||||||
|             final Binding<?> binding = handler.handle(qualifier, paramType); |             final Binding<?> binding = handler.handle(qualifier, paramType); | ||||||
|             if (binding != null) { |             if (binding != null) { | ||||||
| @ -165,21 +167,23 @@ public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected final Object resolveInjectedArg(Parameter parameter) { |     protected final Object resolveInjectedArg(CreateContext context, Parameter parameter) { | ||||||
|         final Object qualifierProvidedArg = this.tryQualifiers(parameter); |         final Object qualifierProvidedArg = this.tryQualifiers(parameter); | ||||||
|         if (qualifierProvidedArg != null) { |         if (qualifierProvidedArg != null) { | ||||||
|             this.checkFilters(parameter, qualifierProvidedArg); |             this.checkFilters(parameter, qualifierProvidedArg); | ||||||
|  |             context.getAllResolved().add(new Resolved(parameter.getType(), qualifierProvidedArg)); | ||||||
|             return qualifierProvidedArg; |             return qualifierProvidedArg; | ||||||
|         } else { |         } else { | ||||||
|             final Object created = this.get(parameter.getType()); |             final Object created = this.get(context, parameter.getType()); | ||||||
|             this.checkFilters(parameter, created); |             this.checkFilters(parameter, created); | ||||||
|  |             context.getAllResolved().add(new Resolved(parameter.getType(), created)); | ||||||
|             return created; |             return created; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected final void resolveInjectedArgs(Object[] dest, Parameter[] params) { |     protected final void resolveInjectedArgs(CreateContext context, Object[] dest, Parameter[] params) { | ||||||
|         for (int i = 0; i < params.length; i++) { |         for (int i = 0; i < params.length; i++) { | ||||||
|             dest[i] = this.resolveInjectedArg(params[i]); |             dest[i] = this.resolveInjectedArg(context, params[i]); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -194,7 +198,7 @@ public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory | |||||||
| 
 | 
 | ||||||
|     // TODO: when there is a null arg, we lose the type. Therefore this algorithm breaks. Fix this. |     // TODO: when there is a null arg, we lose the type. Therefore this algorithm breaks. Fix this. | ||||||
|     @Override |     @Override | ||||||
|     protected Object[] createArgs(Constructor<?> constructor, Object[] givenArgs) { |     protected Object[] createArgs(CreateContext context, Constructor<?> constructor, Object[] givenArgs) { | ||||||
|         final Class<?>[] paramTypes = constructor.getParameterTypes(); |         final Class<?>[] paramTypes = constructor.getParameterTypes(); | ||||||
| 
 | 
 | ||||||
|         // check no arg |         // check no arg | ||||||
| @ -218,7 +222,7 @@ public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory | |||||||
| 
 | 
 | ||||||
|         if (givenArgs.length == 0) { |         if (givenArgs.length == 0) { | ||||||
|             // if no given args, then they are all injected |             // if no given args, then they are all injected | ||||||
|             this.resolveInjectedArgs(resolvedArgs, allParams); |             this.resolveInjectedArgs(context, resolvedArgs, allParams); | ||||||
|         } else if (givenArgs.length == paramTypes.length) { |         } else if (givenArgs.length == paramTypes.length) { | ||||||
|             // all are given |             // all are given | ||||||
|             this.resolveGivenArgs(resolvedArgs, allParams, givenArgs, 0); |             this.resolveGivenArgs(resolvedArgs, allParams, givenArgs, 0); | ||||||
| @ -234,17 +238,23 @@ public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory | |||||||
|             final Parameter[] givenParams = new Parameter[allParams.length - firstGivenIndex]; |             final Parameter[] givenParams = new Parameter[allParams.length - firstGivenIndex]; | ||||||
| 
 | 
 | ||||||
|             System.arraycopy(allParams, 0, injectedParams, 0, injectedParams.length); |             System.arraycopy(allParams, 0, injectedParams, 0, injectedParams.length); | ||||||
|             System.arraycopy(allParams, firstGivenIndex, givenParams, 0, allParams.length - firstGivenIndex); |             System.arraycopy( | ||||||
|  |                     allParams, firstGivenIndex, givenParams, 0, allParams.length - firstGivenIndex | ||||||
|  |             ); | ||||||
| 
 | 
 | ||||||
|             this.resolveInjectedArgs(resolvedArgs, injectedParams); |             this.resolveInjectedArgs(context, resolvedArgs, injectedParams); | ||||||
|             this.resolveGivenArgs(resolvedArgs, givenParams, givenArgs, firstGivenIndex); |             this.resolveGivenArgs(resolvedArgs, givenParams, givenArgs, firstGivenIndex); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return resolvedArgs; |         return resolvedArgs; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @SuppressWarnings("unchecked") |  | ||||||
|     private <T> T handleBinding(Binding<T> binding, Object[] constructorArgs) { |     private <T> T handleBinding(Binding<T> binding, Object[] constructorArgs) { | ||||||
|  |         return this.handleBinding(binding, null, constructorArgs); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @SuppressWarnings("unchecked") | ||||||
|  |     private <T> T handleBinding(Binding<T> binding, @Nullable CreateContext context, Object[] constructorArgs) { | ||||||
|         return switch (binding) { |         return switch (binding) { | ||||||
|             case ClassBinding<T>(Class<T> ignored, Class<? extends T> to) -> { |             case ClassBinding<T>(Class<T> ignored, Class<? extends T> to) -> { | ||||||
|                 final Annotation scopeAnnotation = getScopeAnnotation(to); |                 final Annotation scopeAnnotation = getScopeAnnotation(to); | ||||||
| @ -253,12 +263,20 @@ public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory | |||||||
|                     @SuppressWarnings("rawtypes") |                     @SuppressWarnings("rawtypes") | ||||||
|                     final ScopeHandler scopeHandler = this.getInSelfOrParent( |                     final ScopeHandler scopeHandler = this.getInSelfOrParent( | ||||||
|                             f -> f.findScopeHandler(scopeClass), |                             f -> f.findScopeHandler(scopeClass), | ||||||
|                             () -> new RuntimeException("There is no configured ScopeHandler for " + scopeClass.getName()) |                             () -> new RuntimeException( | ||||||
|  |                                     "There is no configured ScopeHandler for " + scopeClass.getName() | ||||||
|  |                             ) | ||||||
|  |                     ); | ||||||
|  |                     final Binding<T> scopedBinding = scopeHandler.onScopedDependencyRequest( | ||||||
|  |                             scopeAnnotation, to, this | ||||||
|                     ); |                     ); | ||||||
|                     final Binding<T> scopedBinding = scopeHandler.onScopedDependencyRequest(scopeAnnotation, to, this); |  | ||||||
|                     yield this.handleBinding(scopedBinding, constructorArgs); |                     yield this.handleBinding(scopedBinding, constructorArgs); | ||||||
|                 } else { |                 } else { | ||||||
|                     yield this.createInstance(to, constructorArgs); |                     if (context != null) { | ||||||
|  |                         yield this.createInstance(context, to, constructorArgs); | ||||||
|  |                     } else { | ||||||
|  |                         yield this.createInstance(to, constructorArgs); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             case ProviderBinding<T> providerBinding -> providerBinding.provider().get(); |             case ProviderBinding<T> providerBinding -> providerBinding.provider().get(); | ||||||
| @ -276,8 +294,8 @@ public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     protected Object getSetterInjectArg(Class<?> targetType, Method setter, Parameter toInject) { |     protected Object getSetterInjectArg(CreateContext context, Class<?> targetType, Method setter, Parameter toInject) { | ||||||
|         return this.resolveInjectedArg(toInject); |         return this.resolveInjectedArg(context, toInject); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -293,7 +311,24 @@ public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory | |||||||
|         if (parentResult != null) { |         if (parentResult != null) { | ||||||
|             return parentResult; |             return parentResult; | ||||||
|         } else { |         } else { | ||||||
|             throw new RuntimeException("No bindings for " + clazz + " with args " + Arrays.toString(constructorArgs) + "."); |             throw new RuntimeException( | ||||||
|  |                     "No bindings for " + clazz + " with args " + Arrays.toString(constructorArgs) + "." | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected <T> T get(CreateContext context, Class<T> type) { | ||||||
|  |         final Binding<T> binding = this.searchRegistry(type); | ||||||
|  |         if (binding != null) { | ||||||
|  |             return this.handleBinding(binding, context, EMPTY_OBJECT_ARRAY); | ||||||
|  |         } | ||||||
|  |         final T parentResult = this.tryParent(type, EMPTY_OBJECT_ARRAY); | ||||||
|  |         if (parentResult != null) { | ||||||
|  |             return parentResult; | ||||||
|  |         } else { | ||||||
|  |             throw new RuntimeException( | ||||||
|  |                     "No bindings for " + type + " with args " + Arrays.toString(EMPTY_OBJECT_ARRAY) + "." | ||||||
|  |             ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,9 +1,3 @@ | |||||||
| package groowt.util.di; | package groowt.util.di; | ||||||
| 
 | 
 | ||||||
| public interface NamedRegistryExtension extends RegistryExtension, KeyBinder<String>, QualifierHandlerContainer { | public interface NamedRegistryExtension extends RegistryExtension, KeyBinder<String>, QualifierHandlerContainer {} | ||||||
| 
 |  | ||||||
|     static <T> KeyHolder<NamedRegistryExtension, String, T> named(String name, Class<T> type) { |  | ||||||
|         return new SimpleKeyHolder<>(NamedRegistryExtension.class, type, name); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ import java.util.function.Consumer; | |||||||
| import java.util.function.Predicate; | import java.util.function.Predicate; | ||||||
| 
 | 
 | ||||||
| public interface Registry extends ExtensionContainer, QualifierHandlerContainer, ScopeHandlerContainer { | public interface Registry extends ExtensionContainer, QualifierHandlerContainer, ScopeHandlerContainer { | ||||||
|  | 
 | ||||||
|     <T> void bind(Class<T> key, Consumer<? super BindingConfigurator<T>> configure); |     <T> void bind(Class<T> key, Consumer<? super BindingConfigurator<T>> configure); | ||||||
|     @Nullable <T> Binding<T> getBinding(Class<T> key); |     @Nullable <T> Binding<T> getBinding(Class<T> key); | ||||||
|     void removeBinding(Class<?> key); |     void removeBinding(Class<?> key); | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| package groowt.util.di; | package groowt.util.di; | ||||||
| 
 | 
 | ||||||
|  | import groovy.lang.Closure; | ||||||
|  | import groovy.lang.DelegatesTo; | ||||||
| import groowt.util.di.filters.FilterHandler; | import groowt.util.di.filters.FilterHandler; | ||||||
| import groowt.util.di.filters.IterableFilterHandler; | import groowt.util.di.filters.IterableFilterHandler; | ||||||
| import jakarta.inject.Provider; | import jakarta.inject.Provider; | ||||||
| @ -16,13 +18,38 @@ import java.util.function.Consumer; | |||||||
| public interface RegistryObjectFactory extends ObjectFactory { | public interface RegistryObjectFactory extends ObjectFactory { | ||||||
| 
 | 
 | ||||||
|     interface Builder<T extends RegistryObjectFactory> { |     interface Builder<T extends RegistryObjectFactory> { | ||||||
|  | 
 | ||||||
|         void configureRegistry(Consumer<? super Registry> configure); |         void configureRegistry(Consumer<? super Registry> configure); | ||||||
|  | 
 | ||||||
|  |         default void configureRegistry(@DelegatesTo(Registry.class) Closure<?> configureClosure) { | ||||||
|  |             this.configureRegistry(registry -> { | ||||||
|  |                 configureClosure.setDelegate(registry); | ||||||
|  |                 configureClosure.call(); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         void addFilterHandler(FilterHandler<?, ?> handler); |         void addFilterHandler(FilterHandler<?, ?> handler); | ||||||
|  | 
 | ||||||
|         void addIterableFilterHandler(IterableFilterHandler<?, ?> handler); |         void addIterableFilterHandler(IterableFilterHandler<?, ?> handler); | ||||||
|  | 
 | ||||||
|         T build(); |         T build(); | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void configureRegistry(Consumer<? super Registry> use); |     Registry getRegistry(); | ||||||
|  | 
 | ||||||
|  |     default void configureRegistry(Consumer<? super Registry> use) { | ||||||
|  |         use.accept(this.getRegistry()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     default void configureRegistry( | ||||||
|  |             @DelegatesTo(value = Registry.class) | ||||||
|  |             Closure<?> configureClosure | ||||||
|  |     ) { | ||||||
|  |         final Registry registry = this.getRegistry(); | ||||||
|  |         configureClosure.setDelegate(registry); | ||||||
|  |         configureClosure.call(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     <A extends Annotation> @Nullable ScopeHandler<A> findScopeHandler(Class<A> scopeType); |     <A extends Annotation> @Nullable ScopeHandler<A> findScopeHandler(Class<A> scopeType); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,8 +1,9 @@ | |||||||
| package groowt.util.di; | package groowt.util.di; | ||||||
| 
 | 
 | ||||||
|  | import jakarta.inject.Provider; | ||||||
| import jakarta.inject.Singleton; | import jakarta.inject.Singleton; | ||||||
| 
 | 
 | ||||||
| import static groowt.util.di.BindingUtil.toSingleton; | import static groowt.util.di.BindingUtil.toLazySingleton; | ||||||
| 
 | 
 | ||||||
| public final class SingletonScopeHandler implements ScopeHandler<Singleton> { | public final class SingletonScopeHandler implements ScopeHandler<Singleton> { | ||||||
| 
 | 
 | ||||||
| @ -19,12 +20,22 @@ public final class SingletonScopeHandler implements ScopeHandler<Singleton> { | |||||||
|             RegistryObjectFactory objectFactory |             RegistryObjectFactory objectFactory | ||||||
|     ) { |     ) { | ||||||
|         final Binding<T> potentialBinding = this.owner.getBinding(dependencyClass); |         final Binding<T> potentialBinding = this.owner.getBinding(dependencyClass); | ||||||
|         if (potentialBinding != null) { |         return switch (potentialBinding) { | ||||||
|             return potentialBinding; |             case ClassBinding<T>(Class<T> from, Class<? extends T> to) -> { | ||||||
|         } else { |                 this.owner.bind(from, toLazySingleton(() -> objectFactory.createInstance(to))); | ||||||
|             this.owner.bind(dependencyClass, toSingleton(objectFactory.createInstance(dependencyClass))); |                 yield this.owner.getBinding(from); | ||||||
|             return this.owner.getBinding(dependencyClass); |             } | ||||||
|         } |             case ProviderBinding<T>(Class<T> from, Provider<? extends T> provider) -> { | ||||||
|  |                 this.owner.bind(from, toLazySingleton(provider::get)); | ||||||
|  |                 yield this.owner.getBinding(from); | ||||||
|  |             } | ||||||
|  |             case SingletonBinding<T> singletonBinding -> singletonBinding; | ||||||
|  |             case LazySingletonBinding<T> lazySingletonBinding -> lazySingletonBinding; | ||||||
|  |             case null -> { | ||||||
|  |                 this.owner.bind(dependencyClass, toLazySingleton(() -> objectFactory.createInstance(dependencyClass))); | ||||||
|  |                 yield this.owner.getBinding(dependencyClass); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|  | |||||||
| @ -2,10 +2,10 @@ package groowt.util.di; | |||||||
| 
 | 
 | ||||||
| import jakarta.inject.Inject; | import jakarta.inject.Inject; | ||||||
| import jakarta.inject.Named; | import jakarta.inject.Named; | ||||||
|  | import jakarta.inject.Singleton; | ||||||
| import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||||
| 
 | 
 | ||||||
| import static groowt.util.di.BindingUtil.*; | import static groowt.util.di.BindingUtil.*; | ||||||
| import static groowt.util.di.NamedRegistryExtension.named; |  | ||||||
| import static org.junit.jupiter.api.Assertions.*; | import static org.junit.jupiter.api.Assertions.*; | ||||||
| 
 | 
 | ||||||
| public class DefaultRegistryObjectFactoryTests { | public class DefaultRegistryObjectFactoryTests { | ||||||
| @ -167,4 +167,109 @@ public class DefaultRegistryObjectFactoryTests { | |||||||
|         assertEquals("Hello, World!", greeter.greet()); |         assertEquals("Hello, World!", greeter.greet()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static final class GreeterDependency { | ||||||
|  | 
 | ||||||
|  |         private GreeterDependencyUser greeter; | ||||||
|  | 
 | ||||||
|  |         @Inject | ||||||
|  |         public void setGreeter(GreeterDependencyUser greeter) { | ||||||
|  |             this.greeter = greeter; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public String filterGreeting() { | ||||||
|  |             return this.greeter.getGreeting().toUpperCase(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static final class GreeterDependencyUser implements Greeter { | ||||||
|  | 
 | ||||||
|  |         private final GreeterDependency greeterDependency; | ||||||
|  | 
 | ||||||
|  |         @Inject | ||||||
|  |         public GreeterDependencyUser(GreeterDependency greeterDependency) { | ||||||
|  |             this.greeterDependency = greeterDependency; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public String greet() { | ||||||
|  |             return this.greeterDependency.filterGreeting(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public String getGreeting() { | ||||||
|  |             return "hello, world!"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void injectedDeferred() { | ||||||
|  |         final var b = DefaultRegistryObjectFactory.Builder.withDefaults(); | ||||||
|  |         b.configureRegistry(r -> { | ||||||
|  |             r.bind(GreeterDependencyUser.class, toSelf()); | ||||||
|  |             r.bind(GreeterDependency.class, toSelf()); | ||||||
|  |         }); | ||||||
|  |         final var f = b.build(); | ||||||
|  |         final var g = f.get(GreeterDependencyUser.class); | ||||||
|  |         assertEquals("HELLO, WORLD!", g.greet()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static final class NoInjectGreeter implements Greeter { | ||||||
|  | 
 | ||||||
|  |         private final String greeting; | ||||||
|  | 
 | ||||||
|  |         public NoInjectGreeter(String greeting) { | ||||||
|  |             this.greeting = greeting; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public String greet() { | ||||||
|  |             return this.greeting; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void noInjectFoundViaGet() { | ||||||
|  |         final var b = DefaultRegistryObjectFactory.Builder.withDefaults(); | ||||||
|  |         b.configureRegistry(r -> { | ||||||
|  |             r.bind(NoInjectGreeter.class, toSelf()); | ||||||
|  |         }); | ||||||
|  |         final var f = b.build(); | ||||||
|  |         final var g = f.get(NoInjectGreeter.class, "Given Greeting"); | ||||||
|  |         assertEquals("Given Greeting", g.greet()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void noInjectFindViaCreate() { | ||||||
|  |         final var b = DefaultRegistryObjectFactory.Builder.withDefaults(); | ||||||
|  |         b.configureRegistry(r -> { | ||||||
|  |             r.bind(NoInjectGreeter.class, toSelf()); | ||||||
|  |         }); | ||||||
|  |         final var f = b.build(); | ||||||
|  |         final var g = f.createInstance(NoInjectGreeter.class, "Given Greeting"); | ||||||
|  |         assertEquals("Given Greeting", g.greet()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Singleton | ||||||
|  |     public static final class SingletonGreeter implements Greeter { | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public String greet() { | ||||||
|  |             return "Hello, World!"; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void singletonDoesNotOverflow() { | ||||||
|  |         final var b = DefaultRegistryObjectFactory.Builder.withDefaults(); | ||||||
|  |         b.configureRegistry(r -> { | ||||||
|  |             r.bind(SingletonGreeter.class, toSelf()); | ||||||
|  |         }); | ||||||
|  |         final var f = b.build(); | ||||||
|  |         final var g = f.get(SingletonGreeter.class); | ||||||
|  |         assertEquals("Hello, World!", g.greet()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user