Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
347c66b4c8 | ||
![]() |
8821e39a24 | ||
![]() |
ac420ce109 | ||
![]() |
526a003533 | ||
![]() |
3087393607 | ||
![]() |
50b04d9c2c | ||
![]() |
869bef51dd | ||
![]() |
db78a190ff | ||
![]() |
2d6eed2591 | ||
![]() |
df395da792 | ||
![]() |
13f85325a1 | ||
![]() |
3202e51d7f | ||
![]() |
abc4b971d6 | ||
![]() |
eb77f0a3d7 | ||
![]() |
71f1547c6a | ||
![]() |
1b63f2807c | ||
![]() |
0c4b42e0ee | ||
![]() |
eb444dda02 | ||
![]() |
dcc1d6e53e | ||
![]() |
1d0de99e1d | ||
![]() |
b4ef7d7da3 | ||
![]() |
21e933d681 | ||
![]() |
7105686f80 | ||
![]() |
0390d15fce | ||
![]() |
9495849dc9 | ||
![]() |
f6071909b6 | ||
![]() |
2b3cd3120c | ||
![]() |
369dc51779 | ||
![]() |
148ced050b | ||
![]() |
589bbca889 | ||
![]() |
2b935da385 | ||
![]() |
7a28b0530d | ||
![]() |
45d188d064 | ||
![]() |
bd4dee98fa | ||
![]() |
1a528465f9 | ||
![]() |
5ce934b3fe | ||
![]() |
74c5698d1b | ||
![]() |
525932668f | ||
![]() |
04866b4d3a | ||
![]() |
8bc3d9d793 | ||
![]() |
55adf223d2 | ||
![]() |
22b929225f | ||
![]() |
49ead642c4 | ||
![]() |
b55cea174d | ||
![]() |
c709932af2 | ||
![]() |
389b0d072c | ||
![]() |
0076ae6627 | ||
![]() |
301f00e9a4 | ||
![]() |
3af495d458 |
20
.gitea/workflows/gitea-ci.yml
Normal file
20
.gitea/workflows/gitea-ci.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
name: Groowt Check and Publish
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout the code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Setup Java.
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: 21
|
||||||
|
- name: Check libraries
|
||||||
|
run: ./gradlew check
|
||||||
|
- name: Publish to git.jessebrault.com
|
||||||
|
run: ./gradlew publishAllPublicationsToGiteaRepository
|
9
README.md
Normal file
9
README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Groowt (Groovy Web Tools/Templates)
|
||||||
|
|
||||||
|
## Project Version-Bumping
|
||||||
|
|
||||||
|
Update the version property in `buildSrc/src/main/groovy/groowt/gradle/groowt-conventions.gradle`.
|
||||||
|
|
||||||
|
## Dependency Versions
|
||||||
|
|
||||||
|
All dependencies and their version refs should be declared in `gradle/libs.versions.toml`.
|
36
TODO.md
36
TODO.md
@ -1,5 +1,17 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
## 0.3.0
|
||||||
|
- [ ] Explore slightly different syntax for web view components to allow better InteliJ and Groovy integration.
|
||||||
|
For example:
|
||||||
|
```
|
||||||
|
@package mysite
|
||||||
|
@import mysite.Component
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<Component componentAttrWithParams={ key, value -> <Echo>$key: $value</Echo>} />
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
- [ ] Separate out the following into separate, non-Groowt projects with their own repositories and the com.jessebrault
|
- [ ] Separate out the following into separate, non-Groowt projects with their own repositories and the com.jessebrault
|
||||||
namespace:
|
namespace:
|
||||||
@ -8,9 +20,31 @@
|
|||||||
- di
|
- di
|
||||||
- extensible
|
- extensible
|
||||||
- fp
|
- fp
|
||||||
|
- [x] Remove cli, groowt-all, groowt-gradle, groowt-gradle-model.
|
||||||
|
- [ ] Get rid of wvc compiler dependency on fp, di.
|
||||||
|
- [ ] Remove as much cruft as possible from web-view-components-compiler, etc.
|
||||||
|
- [ ] Use new namespaces (i.e., packages) the individual projects:
|
||||||
|
- `com.jessebrault.groowt`: for views
|
||||||
|
- `com.jessebrault.groowt.component`: for view-components
|
||||||
|
- `com.jessebrault.groowt.web`: for web-view-components and web-view-components-compiler
|
||||||
|
|
||||||
|
## 0.1.3
|
||||||
|
- [ ] ~~refactor tools/gradle start scripts to use dist instead of custom bin script~~
|
||||||
|
- [ ] ~~have custom bin/* scripts which point to dist(s) for convenience~~
|
||||||
|
- [x] di bug: @Singleton toSelf() causes stack overflow
|
||||||
|
- [x] wvcc bug: Nested static view classes are not seen by compiler
|
||||||
|
- This required tweaking how the configurations are passed around. Ultimately, we should strive for less complexity
|
||||||
|
in this regard.
|
||||||
|
- [x] `OutletContainer` trait or interface for components which can contain an `<Outlet />` child.
|
||||||
|
- [x] `Context` should have methods for simply finding an ancestor of a certain type without the need for a predicate.
|
||||||
|
|
||||||
## 0.1.2
|
## 0.1.2
|
||||||
- [ ] di bug: @Singleton toSelf() causes stack overflow
|
- [x] `Outlet` component for rendering children like so:
|
||||||
|
```
|
||||||
|
<Outlet children={children} />
|
||||||
|
```
|
||||||
|
- [x] `Render` component
|
||||||
|
- [x] `data-` attributes need to function correctly (really any attribute with hyphen).
|
||||||
|
|
||||||
## 0.1.1
|
## 0.1.1
|
||||||
- [x] `Switch` and `Case` components
|
- [x] `Switch` and `Case` components
|
||||||
|
@ -4,10 +4,15 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = 'groowt'
|
group = 'groowt'
|
||||||
version = '0.1.1'
|
version = '0.2.0'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
||||||
|
maven {
|
||||||
|
name = 'Gitea'
|
||||||
|
url = 'https://git.jessebrault.com/api/packages/jessebrault/maven'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
|
@ -4,6 +4,8 @@ import com.jessebrault.jbarchiva.JbArchivaPlugin
|
|||||||
import org.gradle.api.Plugin
|
import org.gradle.api.Plugin
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
|
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
|
||||||
|
import org.gradle.api.credentials.HttpHeaderCredentials
|
||||||
|
import org.gradle.authentication.http.HttpHeaderAuthentication
|
||||||
|
|
||||||
class GroowtPublishPlugin implements Plugin<Project> {
|
class GroowtPublishPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
@ -11,6 +13,25 @@ class GroowtPublishPlugin implements Plugin<Project> {
|
|||||||
void apply(Project project) {
|
void apply(Project project) {
|
||||||
project.plugins.apply(MavenPublishPlugin)
|
project.plugins.apply(MavenPublishPlugin)
|
||||||
project.plugins.apply(JbArchivaPlugin)
|
project.plugins.apply(JbArchivaPlugin)
|
||||||
|
project.with {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'groowt-conventions'
|
|
||||||
id 'groowt-logging'
|
|
||||||
id 'groowt-publish'
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
url 'https://repo.gradle.org/gradle/libs-releases'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation libs.gradle.tooling
|
|
||||||
implementation libs.picocli
|
|
||||||
implementation project(':groowt-gradle-model')
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named('jar', Jar) {
|
|
||||||
manifest {
|
|
||||||
attributes('Main-Class': 'groowt.cli.GroowtCli')
|
|
||||||
}
|
|
||||||
from sourceSets.main.runtimeClasspath.filter(File.&exists).collect { it.isDirectory() ? it : zipTree(it) }
|
|
||||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
|
||||||
dependsOn ':groowt-gradle-model:jar'
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(GenerateModuleMetadata).configureEach {
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
create('groowtCli', MavenPublication) {
|
|
||||||
artifactId = 'groowt-cli'
|
|
||||||
from components.java
|
|
||||||
pom {
|
|
||||||
withXml {
|
|
||||||
def rootNode = asNode()
|
|
||||||
def dependenciesNode = rootNode.get('dependencies')
|
|
||||||
rootNode.remove(dependenciesNode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package groowt.cli;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public final class FileAndPathUtil {
|
|
||||||
|
|
||||||
public static File packageNameToFile(String packageName) {
|
|
||||||
return new File(packageName.replace(".", File.separator));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File resolve(File from, File to) {
|
|
||||||
return from.toPath().resolve(to.toPath()).toFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileAndPathUtil() {}
|
|
||||||
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package groowt.cli;
|
|
||||||
|
|
||||||
import picocli.CommandLine;
|
|
||||||
import picocli.CommandLine.Command;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
@Command(name = "generate", aliases = "gen", description = "Generate a component, template, model, etc.")
|
|
||||||
public final class Generate implements Callable<Integer> {
|
|
||||||
|
|
||||||
@CommandLine.ParentCommand
|
|
||||||
private GroowtCli cli;
|
|
||||||
|
|
||||||
@CommandLine.Option(
|
|
||||||
names = { "-c", "--component" },
|
|
||||||
description = "Create a component with the given name."
|
|
||||||
)
|
|
||||||
private String componentName;
|
|
||||||
|
|
||||||
@CommandLine.Option(
|
|
||||||
names = { "-s", "--sourceSet" },
|
|
||||||
description = "The source set in which to generate the component, etc.",
|
|
||||||
defaultValue = "main"
|
|
||||||
)
|
|
||||||
private String sourceSet;
|
|
||||||
|
|
||||||
@CommandLine.Option(
|
|
||||||
names = { "-d", "--srcDir", "--sourceDir", "--sourceDirectory" },
|
|
||||||
description = "The directory in the source set in which to generate the component, etc."
|
|
||||||
)
|
|
||||||
private File sourceDir;
|
|
||||||
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
|
||||||
@Override
|
|
||||||
public Integer call() {
|
|
||||||
if (this.componentName != null) {
|
|
||||||
GradleUtil.doWithGroowtGradleModel(this.cli.getProjectDir(), model -> {
|
|
||||||
if (this.sourceDir == null) {
|
|
||||||
this.sourceDir = new File(String.join(File.separator, "src", this.sourceSet, "groovy"));
|
|
||||||
}
|
|
||||||
final File packageDir = FileAndPathUtil.resolve(
|
|
||||||
this.sourceDir,
|
|
||||||
FileAndPathUtil.packageNameToFile(model.getBasePackage())
|
|
||||||
);
|
|
||||||
packageDir.mkdirs();
|
|
||||||
final File componentFile = new File(packageDir, this.componentName + ".txt");
|
|
||||||
try (final OutputStream componentFileOutputStream = new FileOutputStream(componentFile)) {
|
|
||||||
componentFileOutputStream.write("Hello, Groowt!".getBytes());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package groowt.cli;
|
|
||||||
|
|
||||||
import groowt.gradle.model.GroowtGradleModel;
|
|
||||||
import org.gradle.tooling.GradleConnector;
|
|
||||||
import org.gradle.tooling.ProjectConnection;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public final class GradleUtil {
|
|
||||||
|
|
||||||
public static void doWith(File projectDir, Consumer<? super ProjectConnection> action) {
|
|
||||||
final var gradleConnector = GradleConnector.newConnector().forProjectDirectory(projectDir);
|
|
||||||
try (final var projectConnection = gradleConnector.connect()) {
|
|
||||||
action.accept(projectConnection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> void doWith(File projectDir, Class<? extends T> modelClass, Consumer<? super T> modelConsumer) {
|
|
||||||
doWith(projectDir, projectConnection -> {
|
|
||||||
final T model = projectConnection.getModel(modelClass);
|
|
||||||
modelConsumer.accept(model);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void doWithGroowtGradleModel(File projectDir, Consumer<? super GroowtGradleModel> modelConsumer) {
|
|
||||||
doWith(projectDir, GroowtGradleModel.class, modelConsumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private GradleUtil() {}
|
|
||||||
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
package groowt.cli;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import picocli.CommandLine;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
@CommandLine.Command(
|
|
||||||
name = "groowt",
|
|
||||||
description = "The command line interface facilitating development of a Groowt project.",
|
|
||||||
mixinStandardHelpOptions = true,
|
|
||||||
version = "0.1.0",
|
|
||||||
subcommands = { Generate.class }
|
|
||||||
)
|
|
||||||
public final class GroowtCli {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(GroowtCli.class);
|
|
||||||
|
|
||||||
@CommandLine.Option(
|
|
||||||
names = { "-v", "--verbose" },
|
|
||||||
description = "Log verbosely to standard out."
|
|
||||||
)
|
|
||||||
private boolean verbose;
|
|
||||||
|
|
||||||
@CommandLine.Option(
|
|
||||||
names = { "--projectDir" },
|
|
||||||
defaultValue = ".",
|
|
||||||
description = "The root directory of the groowt project."
|
|
||||||
)
|
|
||||||
private File projectDir;
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
logger.info("Hello from Groowt! Version 0.1.0");
|
|
||||||
System.exit(new CommandLine(new GroowtCli()).execute(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVerbose() {
|
|
||||||
return this.verbose;
|
|
||||||
}
|
|
||||||
|
|
||||||
public File getProjectDir() {
|
|
||||||
return this.projectDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<Configuration xmlns="http://logging.apache.org/log4j/2.0/config">
|
|
||||||
<Appenders>
|
|
||||||
<Console name="root">
|
|
||||||
<PatternLayout>
|
|
||||||
<LevelPatternSelector defaultPattern="[%t] %-5level %logger{1.} %msg%n">
|
|
||||||
<PatternMatch key="DEBUG" pattern="[%t] %-5level %logger{1.}.%M() %msg%n"/>
|
|
||||||
</LevelPatternSelector>
|
|
||||||
</PatternLayout>
|
|
||||||
</Console>
|
|
||||||
</Appenders>
|
|
||||||
<Loggers>
|
|
||||||
<Root level="INFO">
|
|
||||||
<AppenderRef ref="root"/>
|
|
||||||
</Root>
|
|
||||||
</Loggers>
|
|
||||||
</Configuration>
|
|
@ -2,34 +2,40 @@
|
|||||||
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
|
# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
|
||||||
|
|
||||||
[versions]
|
[versions]
|
||||||
antlr = '4.13.1'
|
antlr = '4.13.2'
|
||||||
groovy = '4.0.21'
|
asm = '9.8'
|
||||||
junit = '5.10.2'
|
di = '0.1.0'
|
||||||
kotlin = '1.9.23'
|
extensible = '0.1.0'
|
||||||
log4j = '2.23.1'
|
groovy = '4.0.27'
|
||||||
mockito = '5.11.0'
|
jakarta-inject = '2.0.1'
|
||||||
slf4j = '2.0.12'
|
jansi = '2.4.2'
|
||||||
|
jbarchiva = '0.2.2'
|
||||||
|
jetbrains-annotations = '26.0.2'
|
||||||
|
junit = '5.13.0'
|
||||||
|
kotlin = '1.9.25'
|
||||||
|
log4j = '2.24.3'
|
||||||
|
mockito = '5.18.0'
|
||||||
|
picocli = '4.7.7'
|
||||||
|
slf4j = '2.0.17'
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
antlr = { module = 'org.antlr:antlr4', version.ref = 'antlr' }
|
antlr = { module = 'org.antlr:antlr4', version.ref = 'antlr' }
|
||||||
antlr-runtime = { module = 'org.antlr:antlr4-runtime', version.ref = 'antlr' }
|
antlr-runtime = { module = 'org.antlr:antlr4-runtime', version.ref = 'antlr' }
|
||||||
asm = 'org.ow2.asm:asm:9.7'
|
asm = { module = 'org.ow2.asm:asm', version.ref = 'asm' }
|
||||||
classgraph = 'io.github.classgraph:classgraph:4.8.172'
|
di = { module = 'com.jessebrault.di:di', version.ref = 'di' }
|
||||||
gradle-tooling = 'org.gradle:gradle-tooling-api:8.6'
|
extensible = { module = 'com.jessebrault.extensible:extensible', version.ref = 'extensible' }
|
||||||
groovy = { module = 'org.apache.groovy:groovy', version.ref = 'groovy' }
|
groovy = { module = 'org.apache.groovy:groovy', version.ref = 'groovy' }
|
||||||
groovy-all = { module = 'org.apache.groovy:groovy-all', version.ref = 'groovy' }
|
|
||||||
groovy-console = { module = 'org.apache.groovy:groovy-console', version.ref = 'groovy' }
|
groovy-console = { module = 'org.apache.groovy:groovy-console', version.ref = 'groovy' }
|
||||||
groovy-templates = { module = 'org.apache.groovy:groovy-templates', version.ref = 'groovy' }
|
groovy-templates = { module = 'org.apache.groovy:groovy-templates', version.ref = 'groovy' }
|
||||||
jakarta-inject = 'jakarta.inject:jakarta.inject-api:2.0.1'
|
jakarta-inject = { module = 'jakarta.inject:jakarta.inject-api', version.ref = 'jakarta-inject' }
|
||||||
jansi = 'org.fusesource.jansi:jansi:2.4.1'
|
jansi = { module = 'org.fusesource.jansi:jansi', version.ref = 'jansi' }
|
||||||
jbarchiva = 'com.jessebrault.jbarchiva:jbarchiva:0.2.1'
|
jbarchiva = { module = 'com.jessebrault.jbarchiva:jbarchiva', version.ref = 'jbarchiva' }
|
||||||
jetbrains-anotations = 'org.jetbrains:annotations:24.1.0'
|
jetbrains-anotations = { module = 'org.jetbrains:annotations', version.ref = 'jetbrains-annotations' }
|
||||||
junit-jupiter-api = { module = 'org.junit.jupiter:junit-jupiter-api', version.ref = 'junit' }
|
junit-jupiter-api = { module = 'org.junit.jupiter:junit-jupiter-api', version.ref = 'junit' }
|
||||||
kotlin-stdlib = { module = 'org.jetbrains.kotlin:kotlin-stdlib', version.ref = 'kotlin' }
|
kotlin-stdlib = { module = 'org.jetbrains.kotlin:kotlin-stdlib', version.ref = 'kotlin' }
|
||||||
kotlin-test = { module = 'org.jetbrains.kotlin:kotlin-test', version.ref = 'kotlin' }
|
|
||||||
log4j-core = { module = 'org.apache.logging.log4j:log4j-core', version.ref = 'log4j' }
|
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' }
|
log4j-slf4jBinding = { module = 'org.apache.logging.log4j:log4j-slf4j2-impl', version.ref = 'log4j' }
|
||||||
mockito-core = { module = 'org.mockito:mockito-core', version.ref = 'mockito' }
|
mockito-core = { module = 'org.mockito:mockito-core', version.ref = 'mockito' }
|
||||||
mockito-junit = { module = 'org.mockito:mockito-junit-jupiter', version.ref = 'mockito' }
|
mockito-junit = { module = 'org.mockito:mockito-junit-jupiter', version.ref = 'mockito' }
|
||||||
picocli = 'info.picocli:picocli:4.7.5'
|
picocli = { module = 'info.picocli:picocli', version.ref = 'picocli' }
|
||||||
slf4j-api = { module = 'org.slf4j:slf4j-api', version.ref = 'slf4j' }
|
slf4j-api = { module = 'org.slf4j:slf4j-api', version.ref = 'slf4j' }
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
6
gradlew
vendored
6
gradlew
vendored
@ -15,6 +15,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@ -55,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# 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.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@ -84,7 +86,7 @@ done
|
|||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
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.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
2
gradlew.bat
vendored
2
gradlew.bat
vendored
@ -13,6 +13,8 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'groowt-conventions'
|
|
||||||
id 'groowt-publish'
|
|
||||||
id 'java-library'
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
api project(':views')
|
|
||||||
api project(':view-components')
|
|
||||||
api project(':web-view-components')
|
|
||||||
api project(':di')
|
|
||||||
api project(':extensible')
|
|
||||||
api project(':fp')
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
create('groowtAll', MavenPublication) {
|
|
||||||
artifactId = 'groowt-all'
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'groowt-conventions'
|
|
||||||
id 'java-library'
|
|
||||||
id 'groowt-publish'
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
create('groowtGradleModel', MavenPublication) {
|
|
||||||
artifactId = 'groowt-gradle-model'
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package groowt.gradle.model;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class DefaultGroowtGradleModel implements GroowtGradleModel, Serializable {
|
|
||||||
|
|
||||||
private String basePackage;
|
|
||||||
private Map<String, Set<File>> sourceSetToTemplatesDirs;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getBasePackage() {
|
|
||||||
return this.basePackage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBasePackage(String basePackage) {
|
|
||||||
this.basePackage = Objects.requireNonNull(basePackage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Set<File>> getSourceSetToTemplatesDirs() {
|
|
||||||
return Objects.requireNonNull(this.sourceSetToTemplatesDirs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSourceFileSets(Map<String, Set<File>> sourceSetToTemplateDir) {
|
|
||||||
this.sourceSetToTemplatesDirs = sourceSetToTemplateDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package groowt.gradle.model;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public interface GroowtGradleModel {
|
|
||||||
String getBasePackage();
|
|
||||||
Map<String, Set<File>> getSourceSetToTemplatesDirs();
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'groowt-conventions'
|
|
||||||
id 'java-gradle-plugin'
|
|
||||||
id 'groowt-publish'
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation libs.groovy
|
|
||||||
implementation project(':groowt-gradle-model')
|
|
||||||
}
|
|
||||||
|
|
||||||
gradlePlugin {
|
|
||||||
plugins {
|
|
||||||
create('groowtGradle') {
|
|
||||||
id = 'groowt-gradle'
|
|
||||||
implementationClass = 'groowt.gradle.GroowtGradlePlugin'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
create('groowtGradlePlugin', MavenPublication) {
|
|
||||||
artifactId = 'groowt-gradle'
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package groowt.gradle;
|
|
||||||
|
|
||||||
import org.gradle.api.model.ObjectFactory;
|
|
||||||
import org.gradle.api.provider.Property;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class DefaultGroowtExtension implements GroowtExtension {
|
|
||||||
|
|
||||||
private final Property<String> basePackage;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public DefaultGroowtExtension(ObjectFactory objectFactory) {
|
|
||||||
this.basePackage = objectFactory.property(String.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Property<String> getBasePackage() {
|
|
||||||
return this.basePackage;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package groowt.gradle;
|
|
||||||
|
|
||||||
import org.gradle.api.file.SourceDirectorySet;
|
|
||||||
import org.gradle.api.internal.file.DefaultSourceDirectorySet;
|
|
||||||
import org.gradle.api.internal.tasks.TaskDependencyFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class DefaultTemplateSourceDirectorySet extends DefaultSourceDirectorySet implements TemplateSourceDirectorySet {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public DefaultTemplateSourceDirectorySet(
|
|
||||||
SourceDirectorySet delegate,
|
|
||||||
TaskDependencyFactory taskDependencyFactory
|
|
||||||
) {
|
|
||||||
super(delegate, taskDependencyFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package groowt.gradle;
|
|
||||||
|
|
||||||
import groovy.lang.Closure;
|
|
||||||
import org.gradle.api.Action;
|
|
||||||
import org.gradle.api.file.SourceDirectorySet;
|
|
||||||
import org.gradle.api.model.ObjectFactory;
|
|
||||||
import org.gradle.api.reflect.HasPublicType;
|
|
||||||
import org.gradle.api.reflect.TypeOf;
|
|
||||||
import org.gradle.util.internal.ConfigureUtil;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class DefaultTemplateSourceSet implements TemplateSourceSet, HasPublicType {
|
|
||||||
|
|
||||||
private final TemplateSourceDirectorySet templateSourceDirectorySet;
|
|
||||||
private final SourceDirectorySet allTemplates;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public DefaultTemplateSourceSet(ObjectFactory objectFactory, String name, String displayName) {
|
|
||||||
this.templateSourceDirectorySet = objectFactory.newInstance(
|
|
||||||
DefaultTemplateSourceDirectorySet.class,
|
|
||||||
objectFactory.sourceDirectorySet(name, displayName + " ComponentTemplate sources")
|
|
||||||
);
|
|
||||||
this.templateSourceDirectorySet.getFilter().include("**/*.wvc", "**/*.gst");
|
|
||||||
this.allTemplates = objectFactory.sourceDirectorySet(
|
|
||||||
"all" + name,
|
|
||||||
displayName + " ComponentTemplate sources"
|
|
||||||
);
|
|
||||||
this.allTemplates.source(this.templateSourceDirectorySet);
|
|
||||||
this.allTemplates.getFilter().include("**/*.wvc", "**/*.gst");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TypeOf<?> getPublicType() {
|
|
||||||
return TypeOf.typeOf(TemplateSourceSet.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TemplateSourceDirectorySet getTemplates() {
|
|
||||||
return this.templateSourceDirectorySet;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TemplateSourceSet templates(Action<? super TemplateSourceDirectorySet> action) {
|
|
||||||
action.execute(this.templateSourceDirectorySet);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
@Override
|
|
||||||
public TemplateSourceSet templates(Closure closure) {
|
|
||||||
ConfigureUtil.configure(closure, this.templateSourceDirectorySet);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SourceDirectorySet getAllTemplates() {
|
|
||||||
return this.allTemplates;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package groowt.gradle;
|
|
||||||
|
|
||||||
import org.gradle.api.provider.Property;
|
|
||||||
|
|
||||||
public interface GroowtExtension {
|
|
||||||
Property<String> getBasePackage();
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
package groowt.gradle;
|
|
||||||
|
|
||||||
import groowt.gradle.model.GroowtGradleModelBuilder;
|
|
||||||
import org.gradle.api.Plugin;
|
|
||||||
import org.gradle.api.Project;
|
|
||||||
import org.gradle.api.artifacts.Configuration;
|
|
||||||
import org.gradle.api.file.FileCollection;
|
|
||||||
import org.gradle.api.internal.tasks.DefaultSourceSet;
|
|
||||||
import org.gradle.api.plugins.GroovyPlugin;
|
|
||||||
import org.gradle.api.plugins.JavaPlugin;
|
|
||||||
import org.gradle.api.plugins.JavaPluginExtension;
|
|
||||||
import org.gradle.api.provider.Provider;
|
|
||||||
import org.gradle.api.tasks.SourceSetContainer;
|
|
||||||
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static org.gradle.api.internal.lambdas.SerializableLambdas.spec;
|
|
||||||
|
|
||||||
public class GroowtGradlePlugin implements Plugin<Project> {
|
|
||||||
|
|
||||||
private final ToolingModelBuilderRegistry modelBuilderRegistry;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public GroowtGradlePlugin(ToolingModelBuilderRegistry modelBuilderRegistry) {
|
|
||||||
this.modelBuilderRegistry = modelBuilderRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void apply(Project project) {
|
|
||||||
// Apply java and groovy plugins, if not done already
|
|
||||||
final var pluginManager = project.getPluginManager();
|
|
||||||
pluginManager.apply(JavaPlugin.class);
|
|
||||||
pluginManager.apply(GroovyPlugin.class);
|
|
||||||
|
|
||||||
// Create our groowt configuration for storing the groowt dependencies
|
|
||||||
final Provider<Configuration> groowtConfigurationProvider = project.getConfigurations()
|
|
||||||
.register("groowt", configuration -> {
|
|
||||||
configuration.setCanBeConsumed(false);
|
|
||||||
configuration.setCanBeResolved(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create groowt extension and source sets.
|
|
||||||
final GroowtExtension groowtExtension = project.getExtensions().create(
|
|
||||||
GroowtExtension.class,
|
|
||||||
"groowt",
|
|
||||||
DefaultGroowtExtension.class
|
|
||||||
);
|
|
||||||
groowtExtension.getBasePackage().convention("");
|
|
||||||
|
|
||||||
final JavaPluginExtension javaExtension = project.getExtensions().getByType(JavaPluginExtension.class);
|
|
||||||
final SourceSetContainer javaSourceSets = javaExtension.getSourceSets();
|
|
||||||
|
|
||||||
// data resources, such as texts, json files, sqlite-databases, etc.
|
|
||||||
javaSourceSets.getByName("main", mainSourceSet -> {
|
|
||||||
mainSourceSet.getResources().srcDir("src/data");
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: figure out how we can set the compile behavior for all of these.
|
|
||||||
javaSourceSets.forEach(sourceSet -> {
|
|
||||||
final TemplateSourceSet templateSourceSet = project.getObjects().newInstance(
|
|
||||||
DefaultTemplateSourceSet.class,
|
|
||||||
"wvc",
|
|
||||||
((DefaultSourceSet) sourceSet).getDisplayName()
|
|
||||||
);
|
|
||||||
final TemplateSourceDirectorySet templateSourceDirectorySet = templateSourceSet.getTemplates();
|
|
||||||
sourceSet.getExtensions().add(
|
|
||||||
TemplateSourceDirectorySet.class,
|
|
||||||
"templates",
|
|
||||||
templateSourceDirectorySet
|
|
||||||
);
|
|
||||||
templateSourceDirectorySet.srcDir("src/" + sourceSet.getName() + "/templates");
|
|
||||||
|
|
||||||
// Explicitly capture only a FileCollection in the lambda below for compatibility with configuration-cache.
|
|
||||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
|
||||||
final FileCollection templateSourceFiles = templateSourceDirectorySet;
|
|
||||||
sourceSet.getResources().getFilter().exclude(
|
|
||||||
spec(element -> templateSourceFiles.contains(element.getFile()))
|
|
||||||
);
|
|
||||||
sourceSet.getAllJava().source(templateSourceDirectorySet);
|
|
||||||
sourceSet.getAllSource().source(templateSourceDirectorySet);
|
|
||||||
});
|
|
||||||
|
|
||||||
// create init task
|
|
||||||
project.getTasks().create("groowtInit", GroowtInitTask.class, groowtConfigurationProvider);
|
|
||||||
|
|
||||||
// tooling models for cli
|
|
||||||
this.modelBuilderRegistry.register(new GroowtGradleModelBuilder());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
package groowt.gradle;
|
|
||||||
|
|
||||||
import org.gradle.api.DefaultTask;
|
|
||||||
import org.gradle.api.Project;
|
|
||||||
import org.gradle.api.artifacts.Configuration;
|
|
||||||
import org.gradle.api.plugins.JavaPluginExtension;
|
|
||||||
import org.gradle.api.provider.Provider;
|
|
||||||
import org.gradle.api.tasks.TaskAction;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class GroowtInitTask extends DefaultTask {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(GroowtInitTask.class);
|
|
||||||
|
|
||||||
private static final Set<String> srcDirsToMake = Set.of("groovy", "templates");
|
|
||||||
|
|
||||||
private final Provider<Configuration> groowtConfigurationProvider;
|
|
||||||
private final File binDir;
|
|
||||||
private final File groowtDir;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public GroowtInitTask(Project project, Provider<Configuration> groowtConfigurationProvider) {
|
|
||||||
this.groowtConfigurationProvider = groowtConfigurationProvider;
|
|
||||||
this.binDir = new File(project.getRootDir(), "bin");
|
|
||||||
this.groowtDir = new File(project.getRootDir(), "groowt");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void createBin() throws IOException {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
this.binDir.mkdirs();
|
|
||||||
final var groowtFile = new File(this.binDir, "groowt");
|
|
||||||
try (final InputStream bootstrapInputStream = this.getClass().getResourceAsStream("groowt")) {
|
|
||||||
if (bootstrapInputStream != null) {
|
|
||||||
try (final OutputStream bootstrapOutputStream = new FileOutputStream(groowtFile)) {
|
|
||||||
bootstrapInputStream.transferTo(bootstrapOutputStream);
|
|
||||||
}
|
|
||||||
if (!groowtFile.setExecutable(true)) {
|
|
||||||
logger.warn("Could not set bin/groowt to executable; you will have to do this yourself.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("Could not find groowt shell script.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void createGroowtFolder() throws IOException {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
this.groowtDir.mkdirs();
|
|
||||||
final var groowtConfiguration = this.groowtConfigurationProvider.get();
|
|
||||||
final Set<File> groowtCliFiles = groowtConfiguration.files(dependency -> {
|
|
||||||
final var group = dependency.getGroup();
|
|
||||||
if (group == null || !group.equals("groowt")) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return dependency.getName().equals("groowt-cli");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
final File groowtCliJarFile = groowtCliFiles.stream()
|
|
||||||
.filter(file -> file.getName().endsWith(".jar"))
|
|
||||||
.findFirst()
|
|
||||||
.orElseThrow(() -> new RuntimeException("Could not find groowt-cli jar file."));
|
|
||||||
final File groowtCliJarOutputFile = new File(this.groowtDir, "groowt-cli.jar");
|
|
||||||
try (final InputStream groowtCliJarInputStream = new FileInputStream(groowtCliJarFile)) {
|
|
||||||
try (final OutputStream groowtCliJarOutputStream = new FileOutputStream(groowtCliJarOutputFile)) {
|
|
||||||
groowtCliJarInputStream.transferTo(groowtCliJarOutputStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void createSrcDirs() {
|
|
||||||
final var javaPluginExtension = this.getProject().getExtensions().getByType(JavaPluginExtension.class);
|
|
||||||
javaPluginExtension.getSourceSets().forEach(sourceSet -> {
|
|
||||||
final var srcDirs = sourceSet.getAllSource().getSrcDirs();
|
|
||||||
srcDirs.forEach(srcDir -> {
|
|
||||||
if (!sourceSet.getName().contains("test") && srcDirsToMake.contains(srcDir.getName())) {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
srcDir.mkdirs();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
public void doInit() throws IOException {
|
|
||||||
this.createBin();
|
|
||||||
this.createGroowtFolder();
|
|
||||||
this.createSrcDirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package groowt.gradle;
|
|
||||||
|
|
||||||
import org.gradle.api.file.SourceDirectorySet;
|
|
||||||
|
|
||||||
public interface TemplateSourceDirectorySet extends SourceDirectorySet {}
|
|
@ -1,18 +0,0 @@
|
|||||||
package groowt.gradle;
|
|
||||||
|
|
||||||
import groovy.lang.Closure;
|
|
||||||
import org.gradle.api.Action;
|
|
||||||
import org.gradle.api.file.SourceDirectorySet;
|
|
||||||
|
|
||||||
public interface TemplateSourceSet {
|
|
||||||
|
|
||||||
TemplateSourceDirectorySet getTemplates();
|
|
||||||
|
|
||||||
SourceDirectorySet getAllTemplates();
|
|
||||||
|
|
||||||
TemplateSourceSet templates(Action<? super TemplateSourceDirectorySet> action);
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
TemplateSourceSet templates(Closure closure);
|
|
||||||
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package groowt.gradle.model;
|
|
||||||
|
|
||||||
import groowt.gradle.GroowtExtension;
|
|
||||||
import groowt.gradle.TemplateSourceDirectorySet;
|
|
||||||
import org.gradle.api.Project;
|
|
||||||
import org.gradle.api.plugins.JavaPluginExtension;
|
|
||||||
import org.gradle.api.provider.Property;
|
|
||||||
import org.gradle.tooling.provider.model.ToolingModelBuilder;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class GroowtGradleModelBuilder implements ToolingModelBuilder {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canBuild(String modelName) {
|
|
||||||
return modelName.equals(GroowtGradleModel.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Object buildAll(@NotNull String modelName, Project project) {
|
|
||||||
final DefaultGroowtGradleModel model = new DefaultGroowtGradleModel();
|
|
||||||
final var groowtExtension = project.getExtensions().getByType(GroowtExtension.class);
|
|
||||||
|
|
||||||
// base package
|
|
||||||
final Property<String> basePackage = groowtExtension.getBasePackage();
|
|
||||||
if (!basePackage.isPresent()) {
|
|
||||||
throw new RuntimeException(
|
|
||||||
"The property 'basePackage' must be set under the 'groowt' extension in build.gradle"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
model.setBasePackage(basePackage.get());
|
|
||||||
|
|
||||||
// templates dirs
|
|
||||||
final Map<String, Set<File>> sourceSetToTemplatesDirs = new HashMap<>();
|
|
||||||
final var javaExtension = project.getExtensions().getByType(JavaPluginExtension.class);
|
|
||||||
javaExtension.getSourceSets().forEach(sourceSet -> {
|
|
||||||
final TemplateSourceDirectorySet templateSourceDirectorySet =
|
|
||||||
sourceSet.getExtensions().getByType(TemplateSourceDirectorySet.class);
|
|
||||||
sourceSetToTemplatesDirs.put(sourceSet.getName(), templateSourceDirectorySet.getFiles());
|
|
||||||
});
|
|
||||||
model.setSourceFileSets(sourceSetToTemplatesDirs);
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
java -jar groowt/groowt-cli.jar "$@"
|
|
@ -6,10 +6,4 @@ pluginManagement {
|
|||||||
|
|
||||||
rootProject.name = 'groowt'
|
rootProject.name = 'groowt'
|
||||||
|
|
||||||
include 'cli', 'groowt-all', 'groowt-gradle', 'groowt-gradle-model', 'views', 'view-components',
|
include 'views', 'view-components', 'web-view-components', 'web-view-components-compiler'
|
||||||
'web-view-components', 'web-view-components-compiler'
|
|
||||||
|
|
||||||
file('util').eachDir {
|
|
||||||
include it.name
|
|
||||||
project(":$it.name").projectDir = it
|
|
||||||
}
|
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'groowt-conventions'
|
|
||||||
id 'groowt-testing'
|
|
||||||
id 'groowt-logging'
|
|
||||||
id 'groowt-publish'
|
|
||||||
id 'java-library'
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
api libs.jakarta.inject
|
|
||||||
api libs.groovy
|
|
||||||
compileOnlyApi libs.jetbrains.anotations
|
|
||||||
implementation libs.slf4j.api, libs.groovy
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
archiveBaseName = 'groowt-util-di'
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
create('di', MavenPublication) {
|
|
||||||
artifactId = 'groowt-util-di'
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,357 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static groowt.util.di.ObjectFactoryUtil.toTypes;
|
|
||||||
|
|
||||||
// TODO: maybe inject fields
|
|
||||||
public abstract class AbstractInjectingObjectFactory implements ObjectFactory {
|
|
||||||
|
|
||||||
protected record CachedInjectConstructor<T>(Class<T> clazz, Constructor<T> constructor) {}
|
|
||||||
|
|
||||||
protected record CachedNonInjectConstructor<T>(
|
|
||||||
Class<T> clazz,
|
|
||||||
Constructor<T> constructor,
|
|
||||||
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 Collection<CachedInjectConstructor<?>> cachedInjectConstructors = new ArrayList<>();
|
|
||||||
private final Collection<CachedNonInjectConstructor<?>> cachedNonInjectConstructors = new ArrayList<>();
|
|
||||||
private final Map<Class<?>, Collection<Method>> cachedSetters = new HashMap<>();
|
|
||||||
private final Map<Method, Parameter> cachedSetterParameters = new HashMap<>();
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private <T> @Nullable Constructor<T> findCachedInjectConstructor(Class<T> clazz) {
|
|
||||||
for (final CachedInjectConstructor<?> cachedConstructor : this.cachedInjectConstructors) {
|
|
||||||
if (clazz.equals(cachedConstructor.clazz())) {
|
|
||||||
return (Constructor<T>) cachedConstructor.constructor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @implNote If overridden, please cache any found inject constructors using {@link #putCachedInjectConstructor}.
|
|
||||||
*
|
|
||||||
* @param clazz the {@link Class} in which to search for an <code>{@literal @}Inject</code> annotated constructor.
|
|
||||||
* @return the inject constructor, or {@code null} if none found.
|
|
||||||
* @param <T> the type of the class
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T> @Nullable Constructor<T> findInjectConstructor(Class<T> clazz) {
|
|
||||||
final Constructor<T> cachedInjectConstructor = this.findCachedInjectConstructor(clazz);
|
|
||||||
if (cachedInjectConstructor != null) {
|
|
||||||
return cachedInjectConstructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Constructor<?>[] constructors = this.cachedAllConstructors.computeIfAbsent(clazz, Class::getConstructors);
|
|
||||||
|
|
||||||
final List<Constructor<?>> injectConstructors = Arrays.stream(constructors)
|
|
||||||
.filter(constructor -> constructor.isAnnotationPresent(Inject.class))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
if (injectConstructors.size() > 1) {
|
|
||||||
// one day maybe support multiple inject constructors
|
|
||||||
throw new UnsupportedOperationException("Cannot have more than one @Inject constructor in class: " + clazz);
|
|
||||||
} else if (injectConstructors.size() == 1) {
|
|
||||||
final Constructor<T> injectConstructor = (Constructor<T>) injectConstructors.getFirst();
|
|
||||||
this.putCachedInjectConstructor(new CachedInjectConstructor<>(clazz, injectConstructor));
|
|
||||||
return injectConstructor;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void putCachedInjectConstructor(CachedInjectConstructor<?> cached) {
|
|
||||||
this.cachedInjectConstructors.add(cached);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private <T> @Nullable Constructor<T> findCachedNonInjectConstructor(Class<T> clazz, Class<?>[] paramTypes) {
|
|
||||||
for (final CachedNonInjectConstructor<?> cachedConstructor : this.cachedNonInjectConstructors) {
|
|
||||||
if (clazz.equals(cachedConstructor.clazz()) && Arrays.equals(cachedConstructor.paramTypes(), paramTypes)) {
|
|
||||||
return (Constructor<T>) cachedConstructor.constructor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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}.
|
|
||||||
*
|
|
||||||
* @param clazz the {@link Class} in which to search for a constructor which does
|
|
||||||
* not have an <code>{@literal @}Inject</code> annotation.
|
|
||||||
* @param constructorArgs the given constructor args
|
|
||||||
* @return the found non-inject constructor appropriate for the given constructor args,
|
|
||||||
* or {@code null} if no such constructor exists
|
|
||||||
* @param <T> the type
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected <T> @Nullable Constructor<T> findNonInjectConstructor(Class<T> clazz, Object[] constructorArgs) {
|
|
||||||
final Class<?>[] types = toTypes(constructorArgs);
|
|
||||||
final Constructor<T> cachedConstructor = this.findCachedNonInjectConstructor(clazz, types);
|
|
||||||
if (cachedConstructor != null) {
|
|
||||||
return cachedConstructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Constructor<?>[] constructors = this.cachedAllConstructors.computeIfAbsent(clazz, Class::getConstructors);
|
|
||||||
for (Constructor<?> constructor : constructors) {
|
|
||||||
if (areArgsAssignable(constructor.getParameterTypes(), constructorArgs)) {
|
|
||||||
final Constructor<T> found = (Constructor<T>) constructor;
|
|
||||||
this.putCachedNonInjectConstructor(new CachedNonInjectConstructor<>(clazz, found, types));
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void putCachedNonInjectConstructor(CachedNonInjectConstructor<?> cached) {
|
|
||||||
this.cachedNonInjectConstructors.add(cached);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @implNote Please call {@code super.findConstructor()} first, and then implement custom
|
|
||||||
* constructor finding logic. If the custom logic finds a constructor, please cache it
|
|
||||||
* using either {@link #putCachedNonInjectConstructor} or {@link #putCachedInjectConstructor}.
|
|
||||||
*/
|
|
||||||
protected <T> Constructor<T> findConstructor(Class<T> clazz, Object[] args) {
|
|
||||||
final Constructor<T> injectConstructor = this.findInjectConstructor(clazz);
|
|
||||||
if (injectConstructor != null) {
|
|
||||||
return injectConstructor;
|
|
||||||
}
|
|
||||||
final Constructor<T> nonInjectConstructor = this.findNonInjectConstructor(clazz, args);
|
|
||||||
if (nonInjectConstructor != null) {
|
|
||||||
return nonInjectConstructor;
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Could not find an appropriate constructor for " + clazz.getName()
|
|
||||||
+ " with args " + Arrays.toString(toTypes(args))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Collection<Method> getAllInjectSetters(Class<?> clazz) {
|
|
||||||
final Method[] allMethods = clazz.getMethods();
|
|
||||||
final Collection<Method> injectSetters = new ArrayList<>();
|
|
||||||
for (final var method : allMethods) {
|
|
||||||
if (
|
|
||||||
method.isAnnotationPresent(Inject.class)
|
|
||||||
&& method.getName().startsWith("set")
|
|
||||||
&& !Modifier.isStatic(method.getModifiers())
|
|
||||||
&& method.getParameterCount() == 1
|
|
||||||
) {
|
|
||||||
injectSetters.add(method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return injectSetters;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Collection<Method> getCachedSettersFor(Object target) {
|
|
||||||
return this.cachedSetters.computeIfAbsent(target.getClass(), this::getAllInjectSetters);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Parameter getCachedInjectParameter(Method setter) {
|
|
||||||
return this.cachedSetterParameters.computeIfAbsent(setter, s -> {
|
|
||||||
if (s.getParameterCount() != 1) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Setter " + s.getName() + " has a parameter count other than one (1)!"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return s.getParameters()[0];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private @Nullable Object findInContext(CreateContext context, Class<?> type) {
|
|
||||||
for (final Resolved resolved : context.getAllResolved()) {
|
|
||||||
if (type.isAssignableFrom(resolved.type)) {
|
|
||||||
return resolved.object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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(CreateContext context, Object target) {
|
|
||||||
this.getCachedSettersFor(target).forEach(setter -> this.injectSetter(context, target, setter));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <T> T createInstance(Class<T> type, Object... constructorArgs) {
|
|
||||||
final Constructor<T> constructor = this.findConstructor(type, constructorArgs);
|
|
||||||
final CreateContext context = new CreateContext(type);
|
|
||||||
final Object[] allArgs = this.createArgs(context, constructor, constructorArgs);
|
|
||||||
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); // In the future, we might have an option to ignore exceptions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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[] createArgs(
|
|
||||||
CreateContext context,
|
|
||||||
Constructor<?> constructor,
|
|
||||||
Object[] constructorArgs
|
|
||||||
);
|
|
||||||
|
|
||||||
protected abstract Object getSetterInjectArg(
|
|
||||||
CreateContext context,
|
|
||||||
Class<?> targetType,
|
|
||||||
Method setter,
|
|
||||||
Parameter toInject
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import groowt.util.di.filters.FilterHandler;
|
|
||||||
import groowt.util.di.filters.IterableFilterHandler;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import static groowt.util.di.RegistryObjectFactoryUtil.orElseSupply;
|
|
||||||
|
|
||||||
public abstract class AbstractRegistryObjectFactory extends AbstractInjectingObjectFactory
|
|
||||||
implements RegistryObjectFactory {
|
|
||||||
|
|
||||||
public static abstract class AbstractBuilder<T extends DefaultRegistryObjectFactory> implements Builder<T> {
|
|
||||||
|
|
||||||
private final Collection<FilterHandler<?, ?>> filterHandlers = new ArrayList<>();
|
|
||||||
private final Collection<IterableFilterHandler<?, ?>> iterableFilterHandlers = new ArrayList<>();
|
|
||||||
private final Registry registry;
|
|
||||||
private @Nullable RegistryObjectFactory parent;
|
|
||||||
|
|
||||||
public AbstractBuilder(Registry registry) {
|
|
||||||
this.registry = registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AbstractBuilder() {
|
|
||||||
this.registry = new DefaultRegistry();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Registry getRegistry() {
|
|
||||||
return this.registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Collection<FilterHandler<?, ?>> getFilterHandlers() {
|
|
||||||
return this.filterHandlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Collection<IterableFilterHandler<?, ?>> getIterableFilterHandlers() {
|
|
||||||
return this.iterableFilterHandlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected @Nullable RegistryObjectFactory getParent() {
|
|
||||||
return this.parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configureRegistry(Consumer<? super Registry> configure) {
|
|
||||||
configure.accept(this.registry);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addFilterHandler(FilterHandler<?, ?> handler) {
|
|
||||||
this.filterHandlers.add(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addIterableFilterHandler(IterableFilterHandler<?, ?> handler) {
|
|
||||||
this.iterableFilterHandlers.add(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setParent(@Nullable RegistryObjectFactory parent) {
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final Registry registry;
|
|
||||||
@Nullable private final RegistryObjectFactory parent;
|
|
||||||
|
|
||||||
public AbstractRegistryObjectFactory(Registry registry, @Nullable RegistryObjectFactory parent) {
|
|
||||||
this.registry = registry;
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Registry getRegistry() {
|
|
||||||
return this.registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <A extends Annotation> @Nullable ScopeHandler<A> findScopeHandler(Class<A> scopeType) {
|
|
||||||
return this.registry.getScopeHandler(scopeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <A extends Annotation> @Nullable QualifierHandler<A> findQualifierHandler(Class<A> qualifierType) {
|
|
||||||
return this.registry.getQualifierHandler(qualifierType);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final <T> Optional<T> findInParent(Function<? super RegistryObjectFactory, @Nullable T> finder) {
|
|
||||||
return this.parent != null ? Optional.ofNullable(finder.apply(this.parent)) : Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final <T> Optional<T> findInSelfOrParent(Function<? super RegistryObjectFactory, @Nullable T> finder) {
|
|
||||||
return orElseSupply(
|
|
||||||
finder.apply(this),
|
|
||||||
() -> this.parent != null ? finder.apply(this.parent) : null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final <T> T getInSelfOrParent(
|
|
||||||
Function<? super RegistryObjectFactory, @Nullable T> finder,
|
|
||||||
Supplier<? extends RuntimeException> exceptionSupplier
|
|
||||||
) {
|
|
||||||
return orElseSupply(
|
|
||||||
finder.apply(this),
|
|
||||||
() -> this.parent != null ? finder.apply(this.parent) : null
|
|
||||||
).orElseThrow(exceptionSupplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
sealed public interface Binding<T> permits ClassBinding, ProviderBinding, SingletonBinding, LazySingletonBinding {}
|
|
@ -1,12 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import jakarta.inject.Provider;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public interface BindingConfigurator<T> {
|
|
||||||
void to(Class<? extends T> target);
|
|
||||||
void toProvider(Provider<? extends T> provider);
|
|
||||||
void toSingleton(T target);
|
|
||||||
void toLazySingleton(Supplier<T> singletonSupplier);
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import jakarta.inject.Provider;
|
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public final class BindingUtil {
|
|
||||||
|
|
||||||
public static <T> Consumer<BindingConfigurator<T>> toClass(Class<? extends T> clazz) {
|
|
||||||
return bc -> bc.to(clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Consumer<BindingConfigurator<T>> toProvider(Provider<? extends T> provider) {
|
|
||||||
return bc -> bc.toProvider(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Consumer<BindingConfigurator<T>> toSingleton(T singleton) {
|
|
||||||
return bc -> bc.toSingleton(singleton);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Consumer<BindingConfigurator<T>> toLazySingleton(Supplier<T> singletonSupplier) {
|
|
||||||
return bc -> bc.toLazySingleton(singletonSupplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Consumer<BindingConfigurator<T>> toSelf() {
|
|
||||||
return bc -> {};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> KeyHolder<NamedRegistryExtension, String, T> named(String name, Class<T> type) {
|
|
||||||
return new SimpleKeyHolder<>(NamedRegistryExtension.class, type, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BindingUtil() {}
|
|
||||||
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
public record ClassBinding<T>(Class<T> from, Class<? extends T> to) implements Binding<T> {}
|
|
@ -1,80 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import jakarta.inject.Named;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public class DefaultNamedRegistryExtension implements NamedRegistryExtension {
|
|
||||||
|
|
||||||
protected static class NamedQualifierHandler implements QualifierHandler<Named> {
|
|
||||||
|
|
||||||
private final DefaultNamedRegistryExtension extension;
|
|
||||||
|
|
||||||
public NamedQualifierHandler(DefaultNamedRegistryExtension extension) {
|
|
||||||
this.extension = extension;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> @Nullable Binding<T> handle(Named named, Class<T> dependencyClass) {
|
|
||||||
return this.extension.getBinding(
|
|
||||||
new SimpleKeyHolder<>(NamedRegistryExtension.class, dependencyClass, named.value())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final Map<String, Binding<?>> bindings = new HashMap<>();
|
|
||||||
protected final QualifierHandler<Named> qualifierHandler = this.getNamedQualifierHandler();
|
|
||||||
|
|
||||||
protected QualifierHandler<Named> getNamedQualifierHandler() {
|
|
||||||
return new NamedQualifierHandler(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public @Nullable <A extends Annotation> QualifierHandler<A> getQualifierHandler(Class<A> qualifierType) {
|
|
||||||
return Named.class.equals(qualifierType) ? (QualifierHandler<A>) this.qualifierHandler : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<String> getKeyClass() {
|
|
||||||
return String.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <B extends KeyBinder<String>, T> void bind(KeyHolder<B, ? extends String, T> keyHolder, Consumer<? super BindingConfigurator<T>> configure) {
|
|
||||||
final var configurator = new SimpleBindingConfigurator<>(keyHolder.type());
|
|
||||||
configure.accept(configurator);
|
|
||||||
this.bindings.put(keyHolder.key(), configurator.getBinding());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public @Nullable <B extends KeyBinder<String>, T> Binding<T> getBinding(KeyHolder<B, ? extends String, T> keyHolder) {
|
|
||||||
return (Binding<T>) this.bindings.getOrDefault(keyHolder.key(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <B extends KeyBinder<String>, T> void removeBinding(KeyHolder<B, ? extends String, T> keyHolder) {
|
|
||||||
this.bindings.remove(keyHolder.key());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <B extends KeyBinder<String>, T> void removeBindingIf(KeyHolder<B, ? extends String, T> keyHolder, Predicate<? super Binding<T>> filter) {
|
|
||||||
final String key = keyHolder.key();
|
|
||||||
if (this.bindings.containsKey(key) && filter.test(this.getBinding(keyHolder))) {
|
|
||||||
this.bindings.remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearAllBindings() {
|
|
||||||
this.bindings.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,200 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public class DefaultRegistry implements Registry {
|
|
||||||
|
|
||||||
protected record ClassKeyBinding<T>(Class<T> key, Binding<T> binding) {}
|
|
||||||
|
|
||||||
protected final Collection<ClassKeyBinding<?>> classBindings = new ArrayList<>();
|
|
||||||
protected final Collection<RegistryExtension> extensions = new ArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeBinding(Class<?> key) {
|
|
||||||
this.classBindings.removeIf(classKeyBinding -> classKeyBinding.key().equals(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <T> void removeBindingIf(Class<T> key, Predicate<Binding<T>> filter) {
|
|
||||||
this.classBindings.removeIf(classKeyBinding ->
|
|
||||||
classKeyBinding.key().equals(key) && filter.test((Binding<T>) classKeyBinding.binding())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <E extends RegistryExtension> List<E> getAllRegistryExtensions(Class<E> extensionType) {
|
|
||||||
return this.extensions.stream()
|
|
||||||
.filter(extension -> extensionType.isAssignableFrom(extension.getClass()))
|
|
||||||
.map(extensionType::cast)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private <E extends RegistryExtension> E getOneRegistryExtension(Class<E> extensionType) {
|
|
||||||
final List<E> extensions = this.getAllRegistryExtensions(extensionType);
|
|
||||||
if (extensions.size() == 1) {
|
|
||||||
return extensions.getFirst();
|
|
||||||
} else if (extensions.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("There is no " + extensionType + " registered for this " + this);
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("There is more than one " + extensionType + " registered for this " + this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addExtension(RegistryExtension extension) {
|
|
||||||
final List<? extends RegistryExtension> existing = this.getAllRegistryExtensions(extension.getClass());
|
|
||||||
if (existing.isEmpty()) {
|
|
||||||
this.extensions.add(extension);
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("There is already at least one " + extension.getClass() + " registered in " + this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <E extends RegistryExtension> E getExtension(Class<E> extensionType) {
|
|
||||||
return this.getOneRegistryExtension(extensionType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <E extends RegistryExtension> Collection<E> getExtensions(Class<E> extensionType) {
|
|
||||||
return this.getAllRegistryExtensions(extensionType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeExtension(RegistryExtension extension) {
|
|
||||||
this.extensions.remove(extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable <A extends Annotation> QualifierHandler<A> getQualifierHandler(Class<A> qualifierType) {
|
|
||||||
final List<QualifierHandler<A>> handlers = new ArrayList<>();
|
|
||||||
for (final var extension : this.extensions) {
|
|
||||||
if (extension instanceof QualifierHandlerContainer handlerContainer) {
|
|
||||||
final var handler = handlerContainer.getQualifierHandler(qualifierType);
|
|
||||||
if (handler != null) {
|
|
||||||
handlers.add(handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (handlers.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
} else if (handlers.size() > 1) {
|
|
||||||
throw new RuntimeException("There is more than one QualifierHandler for " + qualifierType.getName());
|
|
||||||
} else {
|
|
||||||
return handlers.getFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable <A extends Annotation> ScopeHandler<A> getScopeHandler(Class<A> scopeType) {
|
|
||||||
final List<ScopeHandler<A>> handlers = new ArrayList<>();
|
|
||||||
for (final var extension : this.extensions) {
|
|
||||||
if (extension instanceof ScopeHandlerContainer handlerContainer) {
|
|
||||||
final var handler = handlerContainer.getScopeHandler(scopeType);
|
|
||||||
if (handler != null) {
|
|
||||||
handlers.add(handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (handlers.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
} else if (handlers.size() > 1) {
|
|
||||||
throw new RuntimeException("There is more than one ScopeHandler for " + scopeType.getName());
|
|
||||||
} else {
|
|
||||||
return handlers.getFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> void bind(Class<T> key, Consumer<? super BindingConfigurator<T>> configure) {
|
|
||||||
final var configurator = new SimpleBindingConfigurator<>(key);
|
|
||||||
configure.accept(configurator);
|
|
||||||
this.classBindings.add(new ClassKeyBinding<>(key, configurator.getBinding()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public @Nullable <T> Binding<T> getBinding(Class<T> key) {
|
|
||||||
for (final var classKeyBinding : this.classBindings) {
|
|
||||||
if (key.isAssignableFrom(classKeyBinding.key())) {
|
|
||||||
return (Binding<T>) classKeyBinding.binding();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private KeyBinder<?> findKeyBinder(Class<?> keyClass) {
|
|
||||||
final List<KeyBinder<?>> binders = new ArrayList<>();
|
|
||||||
for (final var extension : this.extensions) {
|
|
||||||
if (extension instanceof KeyBinder<?> keyBinder && keyBinder.getKeyClass().isAssignableFrom(keyClass)) {
|
|
||||||
binders.add(keyBinder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (binders.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("There are no configured RegistryExtensions that can handle keys with type " + keyClass.getName());
|
|
||||||
} else if (binders.size() > 1) {
|
|
||||||
throw new IllegalArgumentException("There is more than one configured RegistryExtension that can handle keys with type " + keyClass.getName());
|
|
||||||
} else {
|
|
||||||
return binders.getFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
protected final void withKeyBinder(KeyHolder<?, ?, ?> keyHolder, Consumer<KeyBinder> action) {
|
|
||||||
action.accept(this.findKeyBinder(keyHolder.key().getClass()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
protected final <R> @Nullable R tapKeyBinder(
|
|
||||||
KeyHolder<?, ?, ?> keyHolder,
|
|
||||||
Function<KeyBinder, @Nullable R> function
|
|
||||||
) {
|
|
||||||
return function.apply(this.findKeyBinder(keyHolder.key().getClass()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <T> void bind(KeyHolder<?, ?, T> keyHolder, Consumer<? super BindingConfigurator<T>> configure) {
|
|
||||||
this.withKeyBinder(keyHolder, b -> b.bind(keyHolder, configure));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public @Nullable <T> Binding<T> getBinding(KeyHolder<?, ?, T> keyHolder) {
|
|
||||||
return this.tapKeyBinder(keyHolder, b -> b.getBinding(keyHolder));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <T> void removeBinding(KeyHolder<?, ?, T> keyHolder) {
|
|
||||||
this.withKeyBinder(keyHolder, b -> b.removeBinding(keyHolder));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <T> void removeBindingIf(
|
|
||||||
KeyHolder<?, ?, T> keyHolder,
|
|
||||||
Predicate<? super Binding<T>> filter
|
|
||||||
) {
|
|
||||||
this.withKeyBinder(keyHolder, b -> b.removeBindingIf(keyHolder, filter));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearAllBindings() {
|
|
||||||
this.classBindings.clear();
|
|
||||||
for (final var extension : this.extensions) {
|
|
||||||
if (extension instanceof KeyBinder<?> keyBinder) {
|
|
||||||
keyBinder.clearAllBindings();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,348 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import groowt.util.di.filters.FilterHandler;
|
|
||||||
import groowt.util.di.filters.IterableFilterHandler;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Parameter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import static groowt.util.di.RegistryObjectFactoryUtil.*;
|
|
||||||
|
|
||||||
public class DefaultRegistryObjectFactory extends AbstractRegistryObjectFactory {
|
|
||||||
|
|
||||||
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
|
|
||||||
* {@link NamedRegistryExtension} and a {@link SingletonScopeHandler}.
|
|
||||||
*
|
|
||||||
* @return the builder
|
|
||||||
*/
|
|
||||||
public static Builder withDefaults() {
|
|
||||||
final var b = new Builder();
|
|
||||||
|
|
||||||
b.configureRegistry(r -> {
|
|
||||||
r.addExtension(new DefaultNamedRegistryExtension());
|
|
||||||
r.addExtension(new SingletonRegistryExtension(r));
|
|
||||||
});
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a blank builder with a {@link Registry} from the given {@link Supplier}.
|
|
||||||
*/
|
|
||||||
public static Builder withRegistry(Supplier<? extends Registry> registrySupplier) {
|
|
||||||
return new Builder(registrySupplier.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a blank builder which will use {@link DefaultRegistry}.
|
|
||||||
*/
|
|
||||||
public static Builder blank() {
|
|
||||||
return new Builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Builder(Registry registry) {
|
|
||||||
super(registry);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Builder() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DefaultRegistryObjectFactory build() {
|
|
||||||
return new DefaultRegistryObjectFactory(
|
|
||||||
this.getRegistry(),
|
|
||||||
this.getParent(),
|
|
||||||
this.getFilterHandlers(),
|
|
||||||
this.getIterableFilterHandlers()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
|
||||||
|
|
||||||
private final Collection<FilterHandler<?, ?>> filterHandlers;
|
|
||||||
private final Collection<IterableFilterHandler<?, ?>> iterableFilterHandlers;
|
|
||||||
|
|
||||||
protected DefaultRegistryObjectFactory(
|
|
||||||
Registry registry,
|
|
||||||
@Nullable RegistryObjectFactory parent,
|
|
||||||
Collection<? extends FilterHandler<?, ?>> filterHandlers,
|
|
||||||
Collection<? extends IterableFilterHandler<?, ?>> iterableFilterHandlers
|
|
||||||
) {
|
|
||||||
super(registry, parent);
|
|
||||||
this.filterHandlers = new ArrayList<>(filterHandlers);
|
|
||||||
this.filterHandlers.forEach(handler -> checkIsValidFilter(handler.getAnnotationClass()));
|
|
||||||
|
|
||||||
this.iterableFilterHandlers = new ArrayList<>(iterableFilterHandlers);
|
|
||||||
this.iterableFilterHandlers.forEach(handler -> checkIsValidIterableFilter(handler.getAnnotationClass()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the given parameter has any qualifier annotations; if it does,
|
|
||||||
* it delegates finding the desired object to the registered {@link QualifierHandler}.
|
|
||||||
*
|
|
||||||
* @param parameter the parameter
|
|
||||||
* @return the object returned from the {@code QualifierHandler}, or {@code null} if no qualifier
|
|
||||||
* is present or the {@code QualifierHandler} itself returns {@code null}.
|
|
||||||
*
|
|
||||||
* @throws RuntimeException if no {@code QualifierHandler} is registered for a qualifier annotation present on the
|
|
||||||
* given parameter, or if the handler itself throws an exception.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected final @Nullable Object tryQualifiers(Parameter parameter) {
|
|
||||||
final Class<?> paramType = parameter.getType();
|
|
||||||
final List<Annotation> qualifiers = RegistryObjectFactoryUtil.getQualifierAnnotations(
|
|
||||||
parameter.getAnnotations()
|
|
||||||
);
|
|
||||||
if (qualifiers.size() > 1) {
|
|
||||||
throw new RuntimeException("Parameter " + parameter + " cannot have more than one Qualifier annotation.");
|
|
||||||
} else if (qualifiers.size() == 1) {
|
|
||||||
final Annotation qualifier = qualifiers.getFirst();
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
final QualifierHandler handler = this.getInSelfOrParent(
|
|
||||||
f -> f.findQualifierHandler(qualifier.annotationType()),
|
|
||||||
() -> new RuntimeException("There is no configured QualifierHandler for "
|
|
||||||
+ qualifier.annotationType().getName()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
final Binding<?> binding = handler.handle(qualifier, paramType);
|
|
||||||
if (binding != null) {
|
|
||||||
return this.handleBinding(binding, EMPTY_OBJECT_ARRAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no Qualifier or the QualifierHandler didn't return a Binding
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the {@code resolvedArg} against all filters present on the given parameter.
|
|
||||||
*
|
|
||||||
* @param parameter the parameter
|
|
||||||
* @param resolvedArg the resolved argument
|
|
||||||
*
|
|
||||||
* @throws RuntimeException if the {@link FilterHandler} itself throws an exception.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
||||||
protected final void checkFilters(Parameter parameter, Object resolvedArg) {
|
|
||||||
final Annotation[] allAnnotations = parameter.getAnnotations();
|
|
||||||
final Collection<Annotation> filterAnnotations = getFilterAnnotations(allAnnotations);
|
|
||||||
if (!filterAnnotations.isEmpty()) {
|
|
||||||
final Collection<FilterHandler<?, ?>> filtersForParamType = this.filterHandlers.stream()
|
|
||||||
.filter(filterHandler ->
|
|
||||||
filterHandler.getArgumentClass().isAssignableFrom(parameter.getType())
|
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
for (final Annotation filterAnnotation : filterAnnotations) {
|
|
||||||
for (final FilterHandler<?, ?> filterHandler : filtersForParamType) {
|
|
||||||
if (filterAnnotation.annotationType().equals(filterHandler.getAnnotationClass())) {
|
|
||||||
// hopefully we've checked everything
|
|
||||||
((FilterHandler<Annotation, Object>) filterHandler).check(filterAnnotation, resolvedArg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final Collection<Annotation> iterableFilterAnnotations = getIterableFilterAnnotations(allAnnotations);
|
|
||||||
if (!iterableFilterAnnotations.isEmpty() && resolvedArg instanceof Iterable iterable) {
|
|
||||||
for (final var annotation : iterableFilterAnnotations) {
|
|
||||||
this.iterableFilterHandlers.stream()
|
|
||||||
.filter(handler -> handler.getAnnotationClass().equals(annotation.annotationType()))
|
|
||||||
.forEach(handler -> {
|
|
||||||
((IterableFilterHandler<Annotation, Object>) handler).check(annotation, iterable);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final Object resolveInjectedArg(CreateContext context, Parameter parameter) {
|
|
||||||
final Object qualifierProvidedArg = this.tryQualifiers(parameter);
|
|
||||||
if (qualifierProvidedArg != null) {
|
|
||||||
this.checkFilters(parameter, qualifierProvidedArg);
|
|
||||||
context.getAllResolved().add(new Resolved(parameter.getType(), qualifierProvidedArg));
|
|
||||||
return qualifierProvidedArg;
|
|
||||||
} else {
|
|
||||||
final Object created = this.get(context, parameter.getType());
|
|
||||||
this.checkFilters(parameter, created);
|
|
||||||
context.getAllResolved().add(new Resolved(parameter.getType(), created));
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void resolveInjectedArgs(CreateContext context, Object[] dest, Parameter[] params) {
|
|
||||||
for (int i = 0; i < params.length; i++) {
|
|
||||||
dest[i] = this.resolveInjectedArg(context, params[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void resolveGivenArgs(Object[] dest, Parameter[] params, Object[] givenArgs, int startIndex) {
|
|
||||||
for (int i = startIndex; i < dest.length; i++) {
|
|
||||||
final int resolveIndex = i - startIndex;
|
|
||||||
final Object arg = givenArgs[resolveIndex];
|
|
||||||
this.checkFilters(params[resolveIndex], arg);
|
|
||||||
dest[i] = arg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: when there is a null arg, we lose the type. Therefore this algorithm breaks. Fix this.
|
|
||||||
@Override
|
|
||||||
protected Object[] createArgs(CreateContext context, Constructor<?> constructor, Object[] givenArgs) {
|
|
||||||
final Class<?>[] paramTypes = constructor.getParameterTypes();
|
|
||||||
|
|
||||||
// check no arg
|
|
||||||
if (paramTypes.length == 0 && givenArgs.length == 0) {
|
|
||||||
// no args given, none needed, so return empty array
|
|
||||||
return EMPTY_OBJECT_ARRAY;
|
|
||||||
} else if (paramTypes.length == 0) { // implicit that givenArgs.length != 0
|
|
||||||
// zero expected, but got given args
|
|
||||||
throw new RuntimeException(
|
|
||||||
"Expected zero args for constructor " + constructor + " but received " + Arrays.toString(givenArgs)
|
|
||||||
);
|
|
||||||
} else if (givenArgs.length > paramTypes.length) {
|
|
||||||
// expected is more than zero, but received too many given
|
|
||||||
throw new RuntimeException(
|
|
||||||
"Too many args given for constructor " + constructor + "; received " + Arrays.toString(givenArgs)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Parameter[] allParams = constructor.getParameters();
|
|
||||||
final Object[] resolvedArgs = new Object[allParams.length];
|
|
||||||
|
|
||||||
if (givenArgs.length == 0) {
|
|
||||||
// if no given args, then they are all injected
|
|
||||||
this.resolveInjectedArgs(context, resolvedArgs, allParams);
|
|
||||||
} else if (givenArgs.length == paramTypes.length) {
|
|
||||||
// all are given
|
|
||||||
this.resolveGivenArgs(resolvedArgs, allParams, givenArgs, 0);
|
|
||||||
} else {
|
|
||||||
// some are injected, some are given
|
|
||||||
// everything before (non-inclusive) is injected
|
|
||||||
// everything after (inclusive) is given
|
|
||||||
// ex: 1 inject, 1 given -> 2 (allParams) - 1 = 1
|
|
||||||
// ex: 0 inject, 1 given -> 1 - 1 = 0
|
|
||||||
final int firstGivenIndex = allParams.length - givenArgs.length;
|
|
||||||
|
|
||||||
final Parameter[] injectedParams = new Parameter[firstGivenIndex];
|
|
||||||
final Parameter[] givenParams = new Parameter[allParams.length - firstGivenIndex];
|
|
||||||
|
|
||||||
System.arraycopy(allParams, 0, injectedParams, 0, injectedParams.length);
|
|
||||||
System.arraycopy(
|
|
||||||
allParams, firstGivenIndex, givenParams, 0, allParams.length - firstGivenIndex
|
|
||||||
);
|
|
||||||
|
|
||||||
this.resolveInjectedArgs(context, resolvedArgs, injectedParams);
|
|
||||||
this.resolveGivenArgs(resolvedArgs, givenParams, givenArgs, firstGivenIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolvedArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
case ClassBinding<T>(Class<T> ignored, Class<? extends T> to) -> {
|
|
||||||
final Annotation scopeAnnotation = getScopeAnnotation(to);
|
|
||||||
if (scopeAnnotation != null) {
|
|
||||||
final Class<? extends Annotation> scopeClass = scopeAnnotation.annotationType();
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
final ScopeHandler scopeHandler = this.getInSelfOrParent(
|
|
||||||
f -> f.findScopeHandler(scopeClass),
|
|
||||||
() -> new RuntimeException(
|
|
||||||
"There is no configured ScopeHandler for " + scopeClass.getName()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
final Binding<T> scopedBinding = scopeHandler.onScopedDependencyRequest(
|
|
||||||
scopeAnnotation, to, this
|
|
||||||
);
|
|
||||||
yield this.handleBinding(scopedBinding, constructorArgs);
|
|
||||||
} else {
|
|
||||||
if (context != null) {
|
|
||||||
yield this.createInstance(context, to, constructorArgs);
|
|
||||||
} else {
|
|
||||||
yield this.createInstance(to, constructorArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ProviderBinding<T> providerBinding -> providerBinding.provider().get();
|
|
||||||
case SingletonBinding<T> singletonBinding -> singletonBinding.to();
|
|
||||||
case LazySingletonBinding<T> lazySingletonBinding -> lazySingletonBinding.singletonSupplier().get();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final <T> @Nullable Binding<T> searchRegistry(Class<T> from) {
|
|
||||||
return this.registry.getBinding(from);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected @Nullable <T> T tryParent(Class<T> clazz, Object[] constructorArgs) {
|
|
||||||
return this.findInParent(f -> f.getOrNull(clazz, constructorArgs)).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object getSetterInjectArg(CreateContext context, Class<?> targetType, Method setter, Parameter toInject) {
|
|
||||||
return this.resolveInjectedArg(context, toInject);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <T> T get(Class<T> clazz, Object... constructorArgs) {
|
|
||||||
final Binding<T> binding = this.searchRegistry(clazz);
|
|
||||||
if (binding != null) {
|
|
||||||
return this.handleBinding(binding, constructorArgs);
|
|
||||||
}
|
|
||||||
final T parentResult = this.tryParent(clazz, constructorArgs);
|
|
||||||
if (parentResult != null) {
|
|
||||||
return parentResult;
|
|
||||||
} else {
|
|
||||||
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) + "."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <T> T getOrDefault(Class<T> clazz, T defaultValue, Object... constructorArgs) {
|
|
||||||
final Binding<T> binding = this.searchRegistry(clazz);
|
|
||||||
if (binding != null) {
|
|
||||||
return this.handleBinding(binding, constructorArgs);
|
|
||||||
}
|
|
||||||
final T parentResult = this.tryParent(clazz, constructorArgs);
|
|
||||||
return parentResult != null ? parentResult : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
public interface ExtensionContainer {
|
|
||||||
void addExtension(RegistryExtension extension);
|
|
||||||
<E extends RegistryExtension> E getExtension(Class<E> extensionType);
|
|
||||||
<E extends RegistryExtension> Collection<E> getExtensions(Class<E> extensionType);
|
|
||||||
void removeExtension(RegistryExtension extension);
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.function.BiPredicate;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public interface KeyBinder<K> {
|
|
||||||
Class<K> getKeyClass();
|
|
||||||
<B extends KeyBinder<K>, T> void bind(KeyHolder<B, ? extends K, T> keyHolder, Consumer<? super BindingConfigurator<T>> configure);
|
|
||||||
<B extends KeyBinder<K>, T> @Nullable Binding<T> getBinding(KeyHolder<B, ? extends K, T> keyHolder);
|
|
||||||
<B extends KeyBinder<K>, T> void removeBinding(KeyHolder<B, ? extends K, T> keyHolder);
|
|
||||||
<B extends KeyBinder<K>, T> void removeBindingIf(KeyHolder<B, ? extends K, T> keyHolder, Predicate<? super Binding<T>> filter);
|
|
||||||
void clearAllBindings();
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
public interface KeyHolder<B extends KeyBinder<K>, K, T> {
|
|
||||||
Class<B> binderType();
|
|
||||||
Class<T> type();
|
|
||||||
K key();
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public record LazySingletonBinding<T>(Supplier<T> singletonSupplier) implements Binding<T> {}
|
|
@ -1,3 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
public interface NamedRegistryExtension extends RegistryExtension, KeyBinder<String>, QualifierHandlerContainer {}
|
|
@ -1,61 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An {@link ObjectFactory} is an object that can construct objects of given types.
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface ObjectFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance of the given {@code instanceType} with the given constructor args.
|
|
||||||
*
|
|
||||||
* @apiNote An implementation may provide a subclass of the given instance type,
|
|
||||||
* or it may directly instantiate the given type, if it is a class
|
|
||||||
* and it can determine the correct constructor from the given arguments.
|
|
||||||
* See individual implementation documentation for exact behavior.
|
|
||||||
*
|
|
||||||
* @implSpec It is up to individual implementations of {@link ObjectFactory} to determine how to
|
|
||||||
* select the appropriate constructor for the given type. The returned
|
|
||||||
* instance must be new and in a valid state.
|
|
||||||
*
|
|
||||||
* @param instanceType the {@link Class} of the desired type
|
|
||||||
* @param constructorArgs any arguments to pass to the constructor(s) of the class.
|
|
||||||
* @return the new instance
|
|
||||||
* @param <T> the desired type
|
|
||||||
*/
|
|
||||||
@Contract("_, _-> new")
|
|
||||||
<T> T createInstance(Class<T> instanceType, Object... constructorArgs);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Very similar to {@link #createInstance(Class, Object...)}, but catches any {@link RuntimeException}
|
|
||||||
* thrown by {@link #createInstance} and subsequently passes it to the given {@link Function}, returning
|
|
||||||
* instead the return value of the {@link Function}.
|
|
||||||
*
|
|
||||||
* @param instanceType the desired type of the created instance
|
|
||||||
* @param onException a {@link Function} to handle when an exception occurs and return a value nonetheless
|
|
||||||
* @param constructorArgs arguments to pass to the constructor
|
|
||||||
* @return the created instance
|
|
||||||
* @param <T> the desired type
|
|
||||||
*
|
|
||||||
* @throws RuntimeException if the given {@link Function} itself throws a RuntimeException
|
|
||||||
*
|
|
||||||
* @see #createInstance(Class, Object...)
|
|
||||||
*/
|
|
||||||
@Contract("_, _, _ -> new")
|
|
||||||
default <T> T createInstanceCatching(
|
|
||||||
Class<T> instanceType,
|
|
||||||
Function<? super RuntimeException, ? extends T> onException,
|
|
||||||
Object... constructorArgs
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
return this.createInstance(instanceType, constructorArgs);
|
|
||||||
} catch (RuntimeException runtimeException) {
|
|
||||||
return onException.apply(runtimeException);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
|
||||||
|
|
||||||
@ApiStatus.Internal
|
|
||||||
public final class ObjectFactoryUtil {
|
|
||||||
|
|
||||||
public static Class<?>[] toTypes(Object... objects) {
|
|
||||||
final Class<?>[] types = new Class<?>[objects.length];
|
|
||||||
for (int i = 0; i < objects.length; i++) {
|
|
||||||
final Object o = objects[i];
|
|
||||||
if (o != null) {
|
|
||||||
types[i] = o.getClass();
|
|
||||||
} else {
|
|
||||||
types[i] = Object.class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return types;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectFactoryUtil() {}
|
|
||||||
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import jakarta.inject.Provider;
|
|
||||||
|
|
||||||
public record ProviderBinding<T>(Class<T> to, Provider<? extends T> provider) implements Binding<T> {}
|
|
@ -1,10 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface QualifierHandler<A extends Annotation> {
|
|
||||||
<T> @Nullable Binding<T> handle(A annotation, Class<T> dependencyClass);
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import jakarta.inject.Qualifier;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
|
|
||||||
public interface QualifierHandlerContainer {
|
|
||||||
|
|
||||||
static void checkIsValidQualifier(Class<? extends Annotation> annotationClass) {
|
|
||||||
if (!annotationClass.isAnnotationPresent(Qualifier.class)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"The given qualifier annotation + " + annotationClass + " is itself not annotated with @Qualifier"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<A extends Annotation> @Nullable QualifierHandler<A> getQualifierHandler(Class<A> qualifierType);
|
|
||||||
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public interface Registry extends ExtensionContainer, QualifierHandlerContainer, ScopeHandlerContainer {
|
|
||||||
|
|
||||||
<T> void bind(Class<T> key, Consumer<? super BindingConfigurator<T>> configure);
|
|
||||||
@Nullable <T> Binding<T> getBinding(Class<T> key);
|
|
||||||
void removeBinding(Class<?> key);
|
|
||||||
<T> void removeBindingIf(Class<T> key, Predicate<Binding<T>> filter);
|
|
||||||
|
|
||||||
<T> void bind(KeyHolder<?, ?, T> keyHolder, Consumer<? super BindingConfigurator<T>> configure);
|
|
||||||
<T> @Nullable Binding<T> getBinding(KeyHolder<?, ?, T> keyHolder);
|
|
||||||
<T> void removeBinding(KeyHolder<?, ?, T> keyHolder);
|
|
||||||
<T> void removeBindingIf(KeyHolder<?, ?, T> keyHolder, Predicate<? super Binding<T>> filter);
|
|
||||||
void clearAllBindings();
|
|
||||||
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
public interface RegistryExtension {}
|
|
@ -1,126 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import groovy.lang.Closure;
|
|
||||||
import groovy.lang.DelegatesTo;
|
|
||||||
import groowt.util.di.filters.FilterHandler;
|
|
||||||
import groowt.util.di.filters.IterableFilterHandler;
|
|
||||||
import jakarta.inject.Provider;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link RegistryObjectFactory} is an {@link ObjectFactory} that offers the ability
|
|
||||||
* to provide desired objects based on an instance of
|
|
||||||
* {@link Registry} to determine how to provide those objects.
|
|
||||||
*/
|
|
||||||
public interface RegistryObjectFactory extends ObjectFactory {
|
|
||||||
|
|
||||||
interface Builder<T extends RegistryObjectFactory> {
|
|
||||||
|
|
||||||
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 addIterableFilterHandler(IterableFilterHandler<?, ?> handler);
|
|
||||||
|
|
||||||
T build();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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 QualifierHandler<A> findQualifierHandler(Class<A> qualifierType);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an object with the desired type. How it is retrieved/created
|
|
||||||
* depends upon the {@link Binding} present in this {@link RegistryObjectFactory}'s held
|
|
||||||
* instances of {@link Registry}. The type of the {@link Binding} determines
|
|
||||||
* how the object is fetched:
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>{@link ClassBinding}: A new instance of the object is created using the given {@code constructorArgs}.</li>
|
|
||||||
* <li>{@link ProviderBinding}: An instance of the object is fetched from the bound {@link Provider}.
|
|
||||||
* Whether the instance is new or not depends on the {@link Provider}.</li>
|
|
||||||
* <li>{@link SingletonBinding}: The bound singleton object is returned.</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @implNote If {@code constructorArgs} are provided
|
|
||||||
* and the {@link Binding} for the desired type is not a
|
|
||||||
* {@link ClassBinding}, the implementation should
|
|
||||||
* either throw an exception or log a warning at the least.
|
|
||||||
*
|
|
||||||
* @param clazz the {@link Class} of the desired type
|
|
||||||
* @param constructorArgs As in {@link #createInstance(Class, Object...)},
|
|
||||||
* the arguments which will be used to create the desired object
|
|
||||||
* if the {@link Binding} is a {@link ClassBinding}.
|
|
||||||
* @return an object of the desired type
|
|
||||||
* @param <T> the desired type
|
|
||||||
*
|
|
||||||
* @throws RuntimeException if there is no registered {@link Binding} or there is a problem
|
|
||||||
* fetching or constructing the object.
|
|
||||||
*/
|
|
||||||
<T> T get(Class<T> clazz, Object... constructorArgs);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Similarly to {@link #get(Class, Object...)}, fetches an object
|
|
||||||
* of the desired type, but does not throw if there is no registered {@link Binding}
|
|
||||||
* in any of the held instances of {@link Registry},
|
|
||||||
* and instead returns the given {@code defaultValue}.
|
|
||||||
*
|
|
||||||
* @param clazz the {@link Class} of the desired type
|
|
||||||
* @param defaultValue the defaultValue to return
|
|
||||||
* @param constructorArgs see {@link #get(Class, Object...)}
|
|
||||||
* @return an object of the desired type
|
|
||||||
* @param <T> the desired type
|
|
||||||
*
|
|
||||||
* @throws RuntimeException if there <em>is</em> a registered {@link Binding} and there is a problem
|
|
||||||
* fetching or constructing the object.
|
|
||||||
*
|
|
||||||
* @see #get(Class, Object...)
|
|
||||||
*/
|
|
||||||
<T> T getOrDefault(Class<T> clazz, T defaultValue, Object... constructorArgs);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Similar to {@link #getOrDefault(Class, Object, Object...)}, except that
|
|
||||||
* it returns null by default if there is no registered {@link Binding}.
|
|
||||||
*
|
|
||||||
* @param clazz the {@link Class} of the desired type
|
|
||||||
* @param constructorArgs see {@link RegistryObjectFactory#get(Class, Object...)}
|
|
||||||
* @return an object of the desired type
|
|
||||||
* @param <T> the desired type
|
|
||||||
*
|
|
||||||
* @see RegistryObjectFactory#get(Class, Object...)
|
|
||||||
* @see RegistryObjectFactory#getOrDefault(Class, Object, Object...)
|
|
||||||
*
|
|
||||||
* @throws RuntimeException if there <em>is</em> a registered {@code Binding} and there
|
|
||||||
* is a problem fetching or constructing the object.
|
|
||||||
*/
|
|
||||||
default <T> @Nullable T getOrNull(Class<T> clazz, Object... constructorArgs) {
|
|
||||||
return this.getOrDefault(clazz, null, constructorArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import groowt.util.di.filters.Filter;
|
|
||||||
import groowt.util.di.filters.IterableFilter;
|
|
||||||
import jakarta.inject.Qualifier;
|
|
||||||
import jakarta.inject.Scope;
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
@ApiStatus.Internal
|
|
||||||
public final class RegistryObjectFactoryUtil {
|
|
||||||
|
|
||||||
private RegistryObjectFactoryUtil() {}
|
|
||||||
|
|
||||||
public static List<Annotation> getQualifierAnnotations(Annotation[] annotations) {
|
|
||||||
return Arrays.stream(annotations)
|
|
||||||
.filter(a -> a.annotationType().isAnnotationPresent(Qualifier.class))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Annotation> getFilterAnnotations(Annotation[] annotations) {
|
|
||||||
return Arrays.stream(annotations)
|
|
||||||
.filter(a -> a.annotationType().isAnnotationPresent(Filter.class))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Annotation> getIterableFilterAnnotations(Annotation[] annotations) {
|
|
||||||
return Arrays.stream(annotations)
|
|
||||||
.filter(a -> a.annotationType().isAnnotationPresent(IterableFilter.class))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Optional<T> orElseSupply(T first, Supplier<T> onNullFirst) {
|
|
||||||
return first != null ? Optional.of(first) : Optional.ofNullable(onNullFirst.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkIsValidFilter(Class<? extends Annotation> annotationClass) {
|
|
||||||
if (!annotationClass.isAnnotationPresent(Filter.class)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"The given filter annotation " + annotationClass.getName() + " is itself not annotated with @Filter"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkIsValidIterableFilter(Class<? extends Annotation> annotationClass) {
|
|
||||||
if (!annotationClass.isAnnotationPresent(IterableFilter.class)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"The given iterable filter annotation " + annotationClass.getName() + " is itself not annotated with @IterableFilter"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @Nullable Annotation getScopeAnnotation(Class<?> clazz) {
|
|
||||||
final List<Annotation> scopeAnnotations = Arrays.stream(clazz.getAnnotations())
|
|
||||||
.filter(annotation -> annotation.annotationType().isAnnotationPresent(Scope.class))
|
|
||||||
.toList();
|
|
||||||
if (scopeAnnotations.size() > 1) {
|
|
||||||
throw new RuntimeException(clazz.getName() + " has too many annotations that are themselves annotated with @Scope");
|
|
||||||
}
|
|
||||||
return scopeAnnotations.size() == 1 ? scopeAnnotations.getFirst() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
|
|
||||||
public interface ScopeHandler<A extends Annotation> {
|
|
||||||
<T> Binding<T> onScopedDependencyRequest(A annotation, Class<T> dependencyClass, RegistryObjectFactory objectFactory);
|
|
||||||
void reset();
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import jakarta.inject.Scope;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
|
|
||||||
public interface ScopeHandlerContainer {
|
|
||||||
|
|
||||||
static void checkIsValidScope(Class<? extends Annotation> scope) {
|
|
||||||
if (!scope.isAnnotationPresent(Scope.class)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"The given scope annotation " + scope + " is itself not annotated with @Scope"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<A extends Annotation> @Nullable ScopeHandler<A> getScopeHandler(Class<A> scopeType);
|
|
||||||
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import jakarta.inject.Provider;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class SimpleBindingConfigurator<T> implements BindingConfigurator<T> {
|
|
||||||
|
|
||||||
private final Class<T> from;
|
|
||||||
private @Nullable Binding<T> binding;
|
|
||||||
|
|
||||||
public SimpleBindingConfigurator(Class<T> from) {
|
|
||||||
this.from = from;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Binding<T> getBinding() {
|
|
||||||
return this.binding != null
|
|
||||||
? this.binding
|
|
||||||
: new ClassBinding<>(this.from, this.from); // return SelfBinding in case we never called anything
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void to(Class<? extends T> target) {
|
|
||||||
this.binding = new ClassBinding<>(this.from, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toProvider(Provider<? extends T> provider) {
|
|
||||||
this.binding = new ProviderBinding<>(this.from, provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toSingleton(T target) {
|
|
||||||
this.binding = new SingletonBinding<>(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void toLazySingleton(Supplier<T> singletonSupplier) {
|
|
||||||
this.binding = new LazySingletonBinding<>(singletonSupplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
public record SimpleKeyHolder<B extends KeyBinder<K>, K, T>(Class<B> binderType, Class<T> type, K key)
|
|
||||||
implements KeyHolder<B, K, T> {}
|
|
@ -1,3 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
public record SingletonBinding<T>(T to) implements Binding<T> {}
|
|
@ -1,22 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import jakarta.inject.Singleton;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
|
|
||||||
public class SingletonRegistryExtension implements RegistryExtension, ScopeHandlerContainer {
|
|
||||||
|
|
||||||
private final SingletonScopeHandler handler;
|
|
||||||
|
|
||||||
public SingletonRegistryExtension(Registry owner) {
|
|
||||||
this.handler = new SingletonScopeHandler(owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public @Nullable <A extends Annotation> ScopeHandler<A> getScopeHandler(Class<A> scopeType) {
|
|
||||||
return Singleton.class.isAssignableFrom(scopeType) ? (ScopeHandler<A>) this.handler : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import jakarta.inject.Singleton;
|
|
||||||
|
|
||||||
import static groowt.util.di.BindingUtil.toSingleton;
|
|
||||||
|
|
||||||
public final class SingletonScopeHandler implements ScopeHandler<Singleton> {
|
|
||||||
|
|
||||||
private final Registry owner;
|
|
||||||
|
|
||||||
public SingletonScopeHandler(Registry owner) {
|
|
||||||
this.owner = owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> Binding<T> onScopedDependencyRequest(
|
|
||||||
Singleton annotation,
|
|
||||||
Class<T> dependencyClass,
|
|
||||||
RegistryObjectFactory objectFactory
|
|
||||||
) {
|
|
||||||
final Binding<T> potentialBinding = this.owner.getBinding(dependencyClass);
|
|
||||||
if (potentialBinding != null) {
|
|
||||||
return potentialBinding;
|
|
||||||
} else {
|
|
||||||
this.owner.bind(dependencyClass, toSingleton(objectFactory.createInstance(dependencyClass)));
|
|
||||||
return this.owner.getBinding(dependencyClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
throw new UnsupportedOperationException("Cannot reset the Singleton scope!");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package groowt.util.di.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
@Target(ElementType.PARAMETER)
|
|
||||||
public @interface Given {}
|
|
@ -1,10 +0,0 @@
|
|||||||
package groowt.util.di.filters;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target({ ElementType.ANNOTATION_TYPE })
|
|
||||||
public @interface Filter {}
|
|
@ -1,39 +0,0 @@
|
|||||||
package groowt.util.di.filters;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.BiPredicate;
|
|
||||||
|
|
||||||
public interface FilterHandler<A extends Annotation, T> {
|
|
||||||
|
|
||||||
boolean check(A annotation, T arg);
|
|
||||||
Class<A> getAnnotationClass();
|
|
||||||
Class<T> getArgumentClass();
|
|
||||||
|
|
||||||
default FilterHandler<A, T> and(BiPredicate<A, T> and) {
|
|
||||||
Objects.requireNonNull(and);
|
|
||||||
return new SimpleFilterHandler<>(
|
|
||||||
(a, t) -> this.check(a, t) && and.test(a, t),
|
|
||||||
this.getAnnotationClass(),
|
|
||||||
this.getArgumentClass()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
default FilterHandler<A, T> or(BiPredicate<A, T> or) {
|
|
||||||
Objects.requireNonNull(or);
|
|
||||||
return new SimpleFilterHandler<>(
|
|
||||||
(a, t) -> this.check(a, t) || or.test(a, t),
|
|
||||||
this.getAnnotationClass(),
|
|
||||||
this.getArgumentClass()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
default FilterHandler<A, T> negate() {
|
|
||||||
return new SimpleFilterHandler<>(
|
|
||||||
(a, t) -> !this.check(a, t),
|
|
||||||
this.getAnnotationClass(),
|
|
||||||
this.getArgumentClass()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package groowt.util.di.filters;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
import java.util.function.BiPredicate;
|
|
||||||
|
|
||||||
import static groowt.util.di.filters.FilterUtil.isAssignableToAnyOf;
|
|
||||||
|
|
||||||
public final class FilterHandlers {
|
|
||||||
|
|
||||||
private FilterHandlers() {}
|
|
||||||
|
|
||||||
public static <A extends Annotation, T> FilterHandler<A, T> of(
|
|
||||||
Class<A> annotationClass,
|
|
||||||
Class<T> argClass,
|
|
||||||
BiPredicate<A, T> predicate
|
|
||||||
) {
|
|
||||||
return new SimpleFilterHandler<>(predicate, annotationClass, argClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Filter
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target(ElementType.PARAMETER)
|
|
||||||
public @interface AllowTypes {
|
|
||||||
Class<?>[] value();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> FilterHandler<AllowTypes, T> getAllowsTypesFilterHandler(Class<T> targetType) {
|
|
||||||
return of(
|
|
||||||
AllowTypes.class,
|
|
||||||
targetType,
|
|
||||||
(annotation, target) -> isAssignableToAnyOf(target.getClass(), annotation.value())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package groowt.util.di.filters;
|
|
||||||
|
|
||||||
public final class FilterUtil {
|
|
||||||
|
|
||||||
private FilterUtil() {}
|
|
||||||
|
|
||||||
public static boolean isAssignableToAnyOf(Class<?> subject, Class<?>[] tests) {
|
|
||||||
for (final var test : tests) {
|
|
||||||
if (test.isAssignableFrom(subject)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package groowt.util.di.filters;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target(ElementType.ANNOTATION_TYPE)
|
|
||||||
public @interface IterableFilter {}
|
|
@ -1,8 +0,0 @@
|
|||||||
package groowt.util.di.filters;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
|
|
||||||
public interface IterableFilterHandler<A extends Annotation, E> {
|
|
||||||
boolean check(A annotation, Iterable<E> iterable);
|
|
||||||
Class<A> getAnnotationClass();
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package groowt.util.di.filters;
|
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
|
||||||
import java.util.function.BiPredicate;
|
|
||||||
|
|
||||||
import static groowt.util.di.filters.FilterUtil.isAssignableToAnyOf;
|
|
||||||
|
|
||||||
public final class IterableFilterHandlers {
|
|
||||||
|
|
||||||
private IterableFilterHandlers() {}
|
|
||||||
|
|
||||||
public static <A extends Annotation, E> IterableFilterHandler<A, E> of(
|
|
||||||
Class<A> filterType,
|
|
||||||
BiPredicate<A, E> elementPredicate
|
|
||||||
) {
|
|
||||||
return new SimpleIterableFilterHandler<>(filterType, elementPredicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@IterableFilter
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target(ElementType.PARAMETER)
|
|
||||||
public @interface IterableElementTypes {
|
|
||||||
Class<?>[] value();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IterableFilterHandler<IterableElementTypes, Object> getIterableElementTypesFilterHandler() {
|
|
||||||
return of(
|
|
||||||
IterableElementTypes.class,
|
|
||||||
(annotation, element) -> isAssignableToAnyOf(element.getClass(), annotation.value())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package groowt.util.di.filters;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.function.BiPredicate;
|
|
||||||
|
|
||||||
final class SimpleFilterHandler<A extends Annotation, T> implements FilterHandler<A, T> {
|
|
||||||
|
|
||||||
private final BiPredicate<A, T> predicate;
|
|
||||||
private final Class<A> annotationClass;
|
|
||||||
private final Class<T> argClass;
|
|
||||||
|
|
||||||
public SimpleFilterHandler(BiPredicate<A, T> predicate, Class<A> annotationClass, Class<T> argClass) {
|
|
||||||
this.predicate = predicate;
|
|
||||||
this.annotationClass = annotationClass;
|
|
||||||
this.argClass = argClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check(A annotation, T arg) {
|
|
||||||
return this.predicate.test(annotation, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<A> getAnnotationClass() {
|
|
||||||
return this.annotationClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<T> getArgumentClass() {
|
|
||||||
return this.argClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package groowt.util.di.filters;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.BiPredicate;
|
|
||||||
|
|
||||||
final class SimpleIterableFilterHandler<A extends Annotation, E> implements IterableFilterHandler<A, E> {
|
|
||||||
|
|
||||||
private final Class<A> annotationClass;
|
|
||||||
private final BiPredicate<A, E> elementPredicate;
|
|
||||||
|
|
||||||
public SimpleIterableFilterHandler(Class<A> annotationClass, BiPredicate<A, E> elementPredicate) {
|
|
||||||
this.annotationClass = annotationClass;
|
|
||||||
this.elementPredicate = elementPredicate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check(A annotation, Iterable<E> iterable) {
|
|
||||||
for (final var e : Objects.requireNonNull(iterable)) {
|
|
||||||
if (!this.elementPredicate.test(annotation, e)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<A> getAnnotationClass() {
|
|
||||||
return this.annotationClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,253 +0,0 @@
|
|||||||
package groowt.util.di;
|
|
||||||
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import jakarta.inject.Named;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static groowt.util.di.BindingUtil.*;
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
public class DefaultRegistryObjectFactoryTests {
|
|
||||||
|
|
||||||
public interface Greeter {
|
|
||||||
String greet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class DefaultGreeter implements Greeter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String greet() {
|
|
||||||
return "Hello, World!";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class GivenArgGreeter implements Greeter {
|
|
||||||
|
|
||||||
private final String greeting;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public GivenArgGreeter(String greeting) {
|
|
||||||
this.greeting = greeting;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String greet() {
|
|
||||||
return this.greeting;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class InjectedArgGreeter implements Greeter {
|
|
||||||
|
|
||||||
private final String greeting;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public InjectedArgGreeter(String greeting) {
|
|
||||||
this.greeting = greeting;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String greet() {
|
|
||||||
return this.greeting;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class InjectedNamedArgGreeter implements Greeter {
|
|
||||||
|
|
||||||
private final String greeting;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public InjectedNamedArgGreeter(@Named("greeting") String greeting) {
|
|
||||||
this.greeting = greeting;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String greet() {
|
|
||||||
return this.greeting;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class InjectedNamedSetterGreeter implements Greeter {
|
|
||||||
|
|
||||||
private String greeting;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public void setGreeting(@Named("greeting") String greeting) {
|
|
||||||
this.greeting = greeting;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String greet() {
|
|
||||||
return this.greeting;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void classSmokeScreen() {
|
|
||||||
final var b = DefaultRegistryObjectFactory.Builder.withDefaults();
|
|
||||||
b.configureRegistry(registry -> {
|
|
||||||
registry.bind(Greeter.class, bc -> bc.to(DefaultGreeter.class));
|
|
||||||
});
|
|
||||||
final RegistryObjectFactory container = b.build();
|
|
||||||
final Greeter greeter = container.get(Greeter.class);
|
|
||||||
assertEquals("Hello, World!", greeter.greet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void singletonSmokeScreen() {
|
|
||||||
final var b = DefaultRegistryObjectFactory.Builder.withDefaults();
|
|
||||||
b.configureRegistry(registry -> {
|
|
||||||
registry.bind(Greeter.class, toSingleton(new DefaultGreeter()));
|
|
||||||
});
|
|
||||||
final RegistryObjectFactory container = b.build();
|
|
||||||
final Greeter greeter = container.get(Greeter.class);
|
|
||||||
assertEquals("Hello, World!", greeter.greet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void providerSmokeScreen() {
|
|
||||||
final var b = DefaultRegistryObjectFactory.Builder.withDefaults();
|
|
||||||
b.configureRegistry(registry -> {
|
|
||||||
registry.bind(Greeter.class, toProvider(DefaultGreeter::new));
|
|
||||||
});
|
|
||||||
final RegistryObjectFactory container = b.build();
|
|
||||||
final Greeter greeter = container.get(Greeter.class);
|
|
||||||
assertEquals("Hello, World!", greeter.greet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void givenArgSmokeScreen() {
|
|
||||||
final var b = DefaultRegistryObjectFactory.Builder.withDefaults();
|
|
||||||
b.configureRegistry(registry -> {
|
|
||||||
registry.bind(Greeter.class, bc -> bc.to(GivenArgGreeter.class));
|
|
||||||
});
|
|
||||||
final RegistryObjectFactory container = b.build();
|
|
||||||
final Greeter greeter = container.get(Greeter.class, "Hello, World!");
|
|
||||||
assertEquals("Hello, World!", greeter.greet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void injectedArg() {
|
|
||||||
final var b = DefaultRegistryObjectFactory.Builder.withDefaults();
|
|
||||||
b.configureRegistry(registry -> {
|
|
||||||
registry.bind(Greeter.class, bc -> bc.to(InjectedArgGreeter.class));
|
|
||||||
registry.bind(String.class, toSingleton("Hello, World!"));
|
|
||||||
});
|
|
||||||
final RegistryObjectFactory container = b.build();
|
|
||||||
final Greeter greeter = container.get(Greeter.class);
|
|
||||||
assertEquals("Hello, World!", greeter.greet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void injectedNamedArg() {
|
|
||||||
final var b = DefaultRegistryObjectFactory.Builder.withDefaults();
|
|
||||||
b.configureRegistry(registry -> {
|
|
||||||
registry.bind(Greeter.class, bc -> bc.to(InjectedNamedArgGreeter.class));
|
|
||||||
registry.bind(named("greeting", String.class), toSingleton("Hello, World!"));
|
|
||||||
});
|
|
||||||
final RegistryObjectFactory container = b.build();
|
|
||||||
final Greeter greeter = container.get(Greeter.class);
|
|
||||||
assertEquals("Hello, World!", greeter.greet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void injectedSetter() {
|
|
||||||
final var b = DefaultRegistryObjectFactory.Builder.withDefaults();
|
|
||||||
b.configureRegistry(r -> {
|
|
||||||
r.bind(Greeter.class, toClass(InjectedNamedSetterGreeter.class));
|
|
||||||
r.bind(named("greeting", String.class), toSingleton("Hello, World!"));
|
|
||||||
});
|
|
||||||
final RegistryObjectFactory f = b.build();
|
|
||||||
final Greeter greeter = f.get(Greeter.class);
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<Configuration xmlns="http://logging.apache.org/log4j/2.0/config">
|
|
||||||
<Appenders>
|
|
||||||
<Console name="root">
|
|
||||||
<PatternLayout>
|
|
||||||
<LevelPatternSelector defaultPattern="[%t] %-5level %logger{1.} %msg%n">
|
|
||||||
<PatternMatch key="DEBUG" pattern="[%t] %-5level %logger{1.}.%M() %msg%n" />
|
|
||||||
</LevelPatternSelector>
|
|
||||||
</PatternLayout>
|
|
||||||
</Console>
|
|
||||||
</Appenders>
|
|
||||||
<Loggers>
|
|
||||||
<Root level="DEBUG">
|
|
||||||
<AppenderRef ref="root" />
|
|
||||||
</Root>
|
|
||||||
</Loggers>
|
|
||||||
</Configuration>
|
|
@ -1,26 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'groowt-conventions'
|
|
||||||
id 'groowt-publish'
|
|
||||||
id 'java-library'
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnlyApi libs.jetbrains.anotations
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
archiveBaseName = 'groowt-util-extensible'
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
create('extensible', MavenPublication) {
|
|
||||||
artifactId = 'groowt-util-extensible'
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
package groowt.util.extensible;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public abstract class AbstractExtensionContainer<E, F> implements ExtensionContainer<E, F> {
|
|
||||||
|
|
||||||
private final F extensionFactory;
|
|
||||||
private final Collection<E> extensions = new ArrayList<>();
|
|
||||||
|
|
||||||
public AbstractExtensionContainer(F extensionFactory) {
|
|
||||||
this.extensionFactory = extensionFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return A <strong>copy</strong> of the registered extensions.
|
|
||||||
*/
|
|
||||||
protected Collection<E> getRegisteredExtensions() {
|
|
||||||
return new ArrayList<>(this.extensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void registerExtension(E extension) {
|
|
||||||
this.extensions.add(extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends E> @Nullable T findExtension(Class<T> extensionClass) {
|
|
||||||
return this.extensions.stream()
|
|
||||||
.filter(extensionClass::isInstance)
|
|
||||||
.findFirst()
|
|
||||||
.map(extensionClass::cast)
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @implNote While this {@link AbstractExtensionContainer} calls
|
|
||||||
* {@link #getExtension}, which will throw if there is no registered
|
|
||||||
* extension, this method may be overridden to not use {@link #getExtension}
|
|
||||||
* and instead implement custom handling logic to avoid throwing, etc.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <T extends E> void configureExtension(Class<T> extensionClass, Consumer<? super T> configure) {
|
|
||||||
configure.accept(this.getExtension(extensionClass));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends E> T getExtension(Class<T> extensionClass) {
|
|
||||||
return this.extensions.stream()
|
|
||||||
.filter(extensionClass::isInstance)
|
|
||||||
.findFirst()
|
|
||||||
.map(extensionClass::cast)
|
|
||||||
.orElseThrow(() -> new IllegalArgumentException("There is no registered extension for " + extensionClass.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasExtension(Class<? extends E> extensionClass) {
|
|
||||||
return this.extensions.stream().anyMatch(extensionClass::isInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public F getExtensionFactory() {
|
|
||||||
return this.extensionFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package groowt.util.extensible;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
// TODO: groovy methods to handle getting extensions via property accessors
|
|
||||||
public interface Extensible<E, F, C extends ExtensionContainer<E, F>> extends ExtensionAware<E> {
|
|
||||||
<T extends E> T createExtension(Class<T> extensionClass, Object... constructorArgs);
|
|
||||||
C getExtensionContainer();
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package groowt.util.extensible;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public interface ExtensionAware<E> {
|
|
||||||
@Nullable <T extends E> T findExtension(Class<T> extensionClass);
|
|
||||||
<T extends E> void configureExtension(Class<T> extensionClass, Consumer<? super T> configure);
|
|
||||||
<T extends E> T getExtension(Class<T> extensionClass);
|
|
||||||
boolean hasExtension(Class<? extends E> extensionClass);
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package groowt.util.extensible;
|
|
||||||
|
|
||||||
public interface ExtensionContainer<E, F> extends ExtensionAware<E> {
|
|
||||||
F getExtensionFactory();
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'groowt-conventions'
|
|
||||||
id 'groowt-publish'
|
|
||||||
id 'java-library'
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
api libs.groovy
|
|
||||||
compileOnlyApi libs.jetbrains.anotations
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
archiveBaseName = 'groowt-util-fp'
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
create('fp', MavenPublication) {
|
|
||||||
artifactId = 'groowt-util-fp'
|
|
||||||
from components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
package groowt.util.fp.either;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public sealed interface Either<E, T> {
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
static <E, T> Either<E, T> left(E error) {
|
|
||||||
return (Either<E, T>) new Left<>(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
static <E, T> Either<E, T> right(T item) {
|
|
||||||
return (Either<E, T>) new Right<>(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
final class Left<E> implements Either<E, Object> {
|
|
||||||
|
|
||||||
private final E error;
|
|
||||||
|
|
||||||
public Left(E error) {
|
|
||||||
this.error = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
public E get() {
|
|
||||||
return this.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
final class Right<T> implements Either<Object, T> {
|
|
||||||
|
|
||||||
private final T item;
|
|
||||||
|
|
||||||
public Right(T item) {
|
|
||||||
this.item = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T get() {
|
|
||||||
return this.item;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean isLeft() {
|
|
||||||
return this instanceof Either.Left;
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean isRight() {
|
|
||||||
return this instanceof Either.Right;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
default Left<E> asLeft() {
|
|
||||||
return (Left<E>) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
default Right<T> asRight() {
|
|
||||||
return (Right<T>) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
default E getLeft() {
|
|
||||||
return this.asLeft().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
default T getRight() {
|
|
||||||
return this.asRight().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
default Either<E, T> mapLeft(Function<? super E, ? extends T> onLeft) {
|
|
||||||
if (this.isLeft()) {
|
|
||||||
return (Either<E, T>) new Right<>(onLeft.apply(this.getLeft()));
|
|
||||||
} else {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
default Either<E, T> flatMapLeft(Function<? super E, Either<E, ? extends T>> onLeft) {
|
|
||||||
if (this.isLeft()) {
|
|
||||||
final var error = this.getLeft();
|
|
||||||
return (Either<E, T>) onLeft.apply(error);
|
|
||||||
} else {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package groowt.util.fp.hkt;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public final class Monoid<T> {
|
|
||||||
|
|
||||||
private final SemiGroup<T> semiGroup;
|
|
||||||
private final Zero<T> zero;
|
|
||||||
|
|
||||||
public Monoid(SemiGroup<T> semiGroup, Zero<T> zero) {
|
|
||||||
this.semiGroup = Objects.requireNonNull(semiGroup);
|
|
||||||
this.zero = Objects.requireNonNull(zero);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SemiGroup<T> getSemiGroup() {
|
|
||||||
return this.semiGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Zero<T> getZero() {
|
|
||||||
return this.zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T concat(T left, T right) {
|
|
||||||
return this.semiGroup.concat(left, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T empty() {
|
|
||||||
return this.zero.getEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package groowt.util.fp.hkt;
|
|
||||||
|
|
||||||
import java.util.function.BinaryOperator;
|
|
||||||
|
|
||||||
public final class SemiGroup<T> {
|
|
||||||
|
|
||||||
private final BinaryOperator<T> concat;
|
|
||||||
|
|
||||||
public SemiGroup(BinaryOperator<T> concat) {
|
|
||||||
this.concat = concat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T concat(T left, T right) {
|
|
||||||
return this.concat.apply(left, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package groowt.util.fp.hkt;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public final class Zero<T> {
|
|
||||||
|
|
||||||
private final T empty;
|
|
||||||
|
|
||||||
public Zero(T empty) {
|
|
||||||
this.empty = Objects.requireNonNull(empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getEmpty() {
|
|
||||||
return this.empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,159 +0,0 @@
|
|||||||
package groowt.util.fp.option;
|
|
||||||
|
|
||||||
import groowt.util.fp.hkt.Monoid;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public abstract sealed class Option<T> {
|
|
||||||
|
|
||||||
private static EmptyOption<Object> emptyInstance;
|
|
||||||
|
|
||||||
public static <T> Option<T> lift(T value) {
|
|
||||||
return new ValueOption<>(Objects.requireNonNull(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Option<T> liftNullable(@Nullable T value) {
|
|
||||||
return value == null ? empty() : lift(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Option<T> liftLazy(Supplier<? extends @NotNull T> valueSupplier) {
|
|
||||||
return new SupplierOption<>(Objects.requireNonNull(valueSupplier));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <T> Option<T> empty() {
|
|
||||||
if (emptyInstance == null) {
|
|
||||||
emptyInstance = new EmptyOption<>();
|
|
||||||
}
|
|
||||||
return (Option<T>) emptyInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class EmptyOption<T> extends Option<T> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T get() {
|
|
||||||
throw new NullPointerException("Cannot get() on EmptyOption");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPresent() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class ValueOption<T> extends Option<T> {
|
|
||||||
|
|
||||||
private final T value;
|
|
||||||
|
|
||||||
public ValueOption(T value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T get() {
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class SupplierOption<T> extends Option<T> {
|
|
||||||
|
|
||||||
private final Supplier<T> valueSupplier;
|
|
||||||
|
|
||||||
public SupplierOption(Supplier<? extends T> valueSupplier) {
|
|
||||||
this.valueSupplier = valueSupplier::get;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T get() {
|
|
||||||
return Objects.requireNonNull(
|
|
||||||
this.valueSupplier.get(),
|
|
||||||
"Cannot get() when the given valueSupplier returns null."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract T get();
|
|
||||||
|
|
||||||
public boolean isPresent() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull T getOrElse(T other) {
|
|
||||||
return this.isPresent() ? this.get() : Objects.requireNonNull(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable T getOrElseNull() {
|
|
||||||
return this.isPresent() ? this.get() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Option<T> orElseLift(T other) {
|
|
||||||
return this.isPresent() ? this : new ValueOption<>(Objects.requireNonNull(other));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Option<T> orElseLiftLazy(Supplier<? extends @NotNull T> lazyOther) {
|
|
||||||
return this.isPresent() ? this : new SupplierOption<>(Objects.requireNonNull(lazyOther));
|
|
||||||
}
|
|
||||||
|
|
||||||
public <U> Option<U> map(Function<? super T, ? extends @NotNull U> mapper) {
|
|
||||||
return new SupplierOption<>(() -> mapper.apply(this.get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public <U> Option<U> mapLazy(Function<? super T, ? extends Supplier<? extends @NotNull U>> lazyMapper) {
|
|
||||||
return new SupplierOption<>(() -> lazyMapper.apply(this.get()).get());
|
|
||||||
}
|
|
||||||
|
|
||||||
public <U> Option<U> flatMap(Function<? super T, Option<? extends U>> mapper) {
|
|
||||||
return new SupplierOption<>(() -> mapper.apply(this.get()).get());
|
|
||||||
}
|
|
||||||
|
|
||||||
public <U> Option<U> flatMapLazy(Function<? super T, Option<Supplier<? extends @NotNull U>>> lazyMapper) {
|
|
||||||
return new SupplierOption<>(() -> lazyMapper.apply(this.get()).get().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ifPresent(Consumer<T> onPresent) {
|
|
||||||
if (this.isPresent()) {
|
|
||||||
onPresent.accept(this.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ifPresentOrElse(Consumer<T> onPresent, Runnable orElse) {
|
|
||||||
if (this.isPresent()) {
|
|
||||||
onPresent.accept(this.get());
|
|
||||||
} else {
|
|
||||||
orElse.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <R> R fold(Function<? super T, ? extends R> onPresent, Supplier<? extends R> onEmpty) {
|
|
||||||
if (this.isPresent()) {
|
|
||||||
return onPresent.apply(this.get());
|
|
||||||
} else {
|
|
||||||
return onEmpty.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <R> R foldMap(Monoid<R> monoid, Function<? super T, ? extends R> onPresent) {
|
|
||||||
if (this.isPresent()) {
|
|
||||||
return onPresent.apply(this.get());
|
|
||||||
} else {
|
|
||||||
return monoid.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <R> R foldFlatMap(Monoid<R> monoid, Function<? super T, Option<? extends R>> onPresent) {
|
|
||||||
if (this.isPresent()) {
|
|
||||||
return onPresent.apply(this.get()).get();
|
|
||||||
} else {
|
|
||||||
return monoid.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
package groowt.util.fp.property;
|
|
||||||
|
|
||||||
import groowt.util.fp.provider.DefaultListProvider;
|
|
||||||
import groowt.util.fp.provider.DefaultProvider;
|
|
||||||
import groowt.util.fp.provider.ListProvider;
|
|
||||||
import groowt.util.fp.provider.Provider;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class DefaultListProperty<T> implements ListProperty<T> {
|
|
||||||
|
|
||||||
public static <T> ListProperty<T> ofType(Class<T> type) {
|
|
||||||
return new DefaultListProperty<>(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Class<T> type;
|
|
||||||
private final List<Provider<T>> elementProviders = new ArrayList<>();
|
|
||||||
|
|
||||||
protected DefaultListProperty(Class<T> type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<T> getType() {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addElement(T element) {
|
|
||||||
this.elementProviders.add(DefaultProvider.of(element));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public void addProvider(Provider<? extends T> elementProvider) {
|
|
||||||
this.elementProviders.add((Provider<T>) elementProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addAllElements(Collection<? extends T> elements) {
|
|
||||||
elements.forEach(this::addElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addAllProviders(Collection<? extends Provider<? extends T>> elementProviders) {
|
|
||||||
elementProviders.forEach(this::addProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <U> ListProvider<U> map(Class<U> targetType, Function<? super T, ? extends U> mapper) {
|
|
||||||
return DefaultListProvider.ofElementProviders(targetType, this.elementProviders.stream()
|
|
||||||
.map(elementProvider -> elementProvider.map(targetType, mapper))
|
|
||||||
.toList()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <U> ListProvider<U> flatMap(
|
|
||||||
Class<U> targetType,
|
|
||||||
Function<? super T, ? extends Provider<? extends U>> flatMapper
|
|
||||||
) {
|
|
||||||
return DefaultListProvider.ofElementProviders(targetType, this.elementProviders.stream()
|
|
||||||
.map(elementProvider -> elementProvider.flatMap(targetType, flatMapper))
|
|
||||||
.toList()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <U extends T> ListProvider<U> withType(Class<U> desiredType) {
|
|
||||||
return DefaultListProvider.ofElementProviders(desiredType, this.elementProviders.stream()
|
|
||||||
.filter(elementProvider -> desiredType.isAssignableFrom(elementProvider.getType()))
|
|
||||||
.map(elementProvider -> elementProvider.map(desiredType, desiredType::cast))
|
|
||||||
.toList()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<T> get() {
|
|
||||||
return this.elementProviders.stream()
|
|
||||||
.map(Provider::get)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
package groowt.util.fp.property;
|
|
||||||
|
|
||||||
import groovy.lang.Closure;
|
|
||||||
import groowt.util.fp.provider.DefaultProvider;
|
|
||||||
import groowt.util.fp.provider.Provider;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
public class DefaultProperty<T> implements Property<T> {
|
|
||||||
|
|
||||||
public static <T> Property<T> empty(Class<T> type) {
|
|
||||||
return new DefaultProperty<>(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <T> Property<T> of(T t) {
|
|
||||||
final Property<T> property = new DefaultProperty<>((Class<T>) t.getClass());
|
|
||||||
property.set(t);
|
|
||||||
return property;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Property<T> ofProvider(Class<T> type, Provider<T> tProvider) {
|
|
||||||
final Property<T> property = new DefaultProperty<>(type);
|
|
||||||
property.set(tProvider);
|
|
||||||
return property;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Property<T> ofLazy(Class<T> type, Supplier<T> tSupplier) {
|
|
||||||
final Property<T> property = new DefaultProperty<>(type);
|
|
||||||
property.set(DefaultProvider.ofLazy(type, tSupplier));
|
|
||||||
return property;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Class<T> type;
|
|
||||||
private final List<Closure<?>> configureClosures = new ArrayList<>();
|
|
||||||
|
|
||||||
private Provider<? extends T> provider;
|
|
||||||
private Provider<? extends T> convention;
|
|
||||||
|
|
||||||
protected DefaultProperty(Class<T> type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<T> getType() {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPresent() {
|
|
||||||
return this.provider != null || this.convention != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return this.provider == null && this.convention == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(T t) {
|
|
||||||
requireNonNull(t);
|
|
||||||
this.provider = DefaultProvider.of(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(Provider<? extends T> tProvider) {
|
|
||||||
requireNonNull(tProvider);
|
|
||||||
this.provider = tProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setConvention(T convention) {
|
|
||||||
requireNonNull(convention);
|
|
||||||
this.convention = DefaultProvider.of(convention);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setConvention(Provider<? extends T> convention) {
|
|
||||||
requireNonNull(convention);
|
|
||||||
this.convention = convention;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configure(Closure<?> configureClosure) {
|
|
||||||
this.configureClosures.add(configureClosure);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doConfigures(T t) {
|
|
||||||
for (final var configureClosure : this.configureClosures) {
|
|
||||||
configureClosure.setDelegate(t);
|
|
||||||
configureClosure.call(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T get() {
|
|
||||||
if (!this.isPresent()) {
|
|
||||||
throw new NullPointerException("Cannot get() from an empty Property. Set the value or set the convention.");
|
|
||||||
}
|
|
||||||
final T t;
|
|
||||||
if (this.provider != null) {
|
|
||||||
t = this.provider.get();
|
|
||||||
} else {
|
|
||||||
t = this.convention.get();
|
|
||||||
}
|
|
||||||
this.doConfigures(t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package groowt.util.fp.property;
|
|
||||||
|
|
||||||
import groowt.util.fp.provider.ListProvider;
|
|
||||||
import groowt.util.fp.provider.Provider;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public interface ListProperty<T> extends ListProvider<T> {
|
|
||||||
|
|
||||||
void addElement(T element);
|
|
||||||
void addProvider(Provider<? extends T> elementProvider);
|
|
||||||
void addAllElements(Collection<? extends T> elements);
|
|
||||||
void addAllProviders(Collection<? extends Provider<? extends T>> elementProviders);
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
default void leftShift(Object object) {
|
|
||||||
Objects.requireNonNull(object);
|
|
||||||
if (object instanceof Provider<?> provider) {
|
|
||||||
if (!this.getType().isAssignableFrom(provider.getType())) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"The type of the given Provider (" + provider.getType().getName() + ") is not compatible with" +
|
|
||||||
"the type of this ListProperty (" + this.getType().getName() + ")."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.addProvider((Provider<? extends T>) provider);
|
|
||||||
} else if (this.getType().isAssignableFrom(object.getClass())) {
|
|
||||||
this.addElement((T) object);
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("The type of the given object (" + object.getClass().getName() +
|
|
||||||
") is not compatible with the type of this ListProperty (" + this.getType().getName() + ")."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package groowt.util.fp.property;
|
|
||||||
|
|
||||||
import groovy.lang.Closure;
|
|
||||||
import groovy.lang.DelegatesTo;
|
|
||||||
import groovy.transform.stc.ClosureParams;
|
|
||||||
import groovy.transform.stc.FromString;
|
|
||||||
import groowt.util.fp.provider.Provider;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public interface Property<T> extends Provider<T> {
|
|
||||||
|
|
||||||
void set(T t);
|
|
||||||
void set(Provider<? extends T> tProvider);
|
|
||||||
void setConvention(T t);
|
|
||||||
void setConvention(Provider<? extends T> tProvider);
|
|
||||||
|
|
||||||
void configure(
|
|
||||||
@DelegatesTo(type = "T")
|
|
||||||
@ClosureParams(value = FromString.class, options = "T")
|
|
||||||
Closure<?> configureClosure
|
|
||||||
);
|
|
||||||
|
|
||||||
boolean isPresent();
|
|
||||||
boolean isEmpty();
|
|
||||||
|
|
||||||
default T fold(@Nullable T onEmpty) {
|
|
||||||
if (this.isPresent()) {
|
|
||||||
return this.get();
|
|
||||||
} else {
|
|
||||||
return onEmpty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default T fold(Provider<? extends T> onEmpty) {
|
|
||||||
if (this.isPresent()) {
|
|
||||||
return this.get();
|
|
||||||
} else {
|
|
||||||
return onEmpty.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
package groowt.util.fp.provider;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class DefaultListProvider<T> implements ListProvider<T> {
|
|
||||||
|
|
||||||
public static <T> ListProvider<T> ofElements(Class<T> elementType, List<? extends T> elements) {
|
|
||||||
return new DefaultListProvider<>(
|
|
||||||
elementType,
|
|
||||||
elements.stream()
|
|
||||||
.<Provider<T>>map(DefaultProvider::of)
|
|
||||||
.toList()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> ListProvider<T> ofElementProviders(
|
|
||||||
Class<T> elementType,
|
|
||||||
List<? extends Provider<T>> elementProviders
|
|
||||||
) {
|
|
||||||
return new DefaultListProvider<>(elementType, elementProviders);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Class<T> elementType;
|
|
||||||
private final List<Provider<T>> elementProviders;
|
|
||||||
|
|
||||||
private DefaultListProvider(Class<T> elementType, List<? extends Provider<T>> elementProviders) {
|
|
||||||
this.elementType = elementType;
|
|
||||||
this.elementProviders = new ArrayList<>(elementProviders);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<T> getType() {
|
|
||||||
return this.elementType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <U> ListProvider<U> map(Class<U> targetType, Function<? super T, ? extends U> mapper) {
|
|
||||||
final List<Provider<U>> uProviders = this.elementProviders.stream()
|
|
||||||
.map(elementProvider -> elementProvider.map(targetType, mapper))
|
|
||||||
.toList();
|
|
||||||
return new DefaultListProvider<>(targetType, uProviders);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <U> ListProvider<U> flatMap(
|
|
||||||
Class<U> targetType,
|
|
||||||
Function<? super T, ? extends Provider<? extends U>> flatMapper
|
|
||||||
) {
|
|
||||||
final List<Provider<U>> uProviders = this.elementProviders.stream()
|
|
||||||
.map(elementProvider -> elementProvider.flatMap(targetType, flatMapper))
|
|
||||||
.toList();
|
|
||||||
return new DefaultListProvider<>(targetType, uProviders);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <U extends T> ListProvider<U> withType(Class<U> desiredType) {
|
|
||||||
return new DefaultListProvider<>(desiredType, this.elementProviders.stream()
|
|
||||||
.filter(elementProvider -> desiredType.isAssignableFrom(elementProvider.getType()))
|
|
||||||
.map(elementProvider -> elementProvider.map(desiredType, desiredType::cast))
|
|
||||||
.toList()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<T> get() {
|
|
||||||
final List<? extends T> filtered = this.elementProviders.stream()
|
|
||||||
.map(Provider::get)
|
|
||||||
.toList();
|
|
||||||
return new ArrayList<>(filtered);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package groowt.util.fp.provider;
|
|
||||||
|
|
||||||
class DefaultNamedProvider<T> implements NamedProvider<T> {
|
|
||||||
|
|
||||||
private final Class<T> type;
|
|
||||||
private final String name;
|
|
||||||
private final Provider<T> delegate;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public DefaultNamedProvider(String name, T element) {
|
|
||||||
this.type = (Class<T>) element.getClass();
|
|
||||||
this.name = name;
|
|
||||||
this.delegate = DefaultProvider.of(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DefaultNamedProvider(Class<T> type, String name, Provider<T> delegate) {
|
|
||||||
this.type = type;
|
|
||||||
this.name = name;
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<T> getType() {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T get() {
|
|
||||||
return this.delegate.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
package groowt.util.fp.provider;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
class DefaultNamedSetProvider<T> extends DefaultSetProvider<T> implements NamedSetProvider<T> {
|
|
||||||
|
|
||||||
public static <T> NamedSetProvider<T> ofElementsAndNames(Class<T> type, Map<String, T> namesAndElements) {
|
|
||||||
return new DefaultNamedSetProvider<>(type, namesAndElements.entrySet().stream()
|
|
||||||
.map(entry -> new DefaultNamedProvider<>(entry.getKey(), entry.getValue()))
|
|
||||||
.collect(Collectors.toSet())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> NamedSetProvider<T> ofNamedProviders(Class<T> type, Set<? extends NamedProvider<T>> providers) {
|
|
||||||
return new DefaultNamedSetProvider<>(type, providers);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected DefaultNamedSetProvider(Class<T> type, Set<? extends NamedProvider<T>> elementProviders) {
|
|
||||||
super(type, elementProviders);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NamedSetProvider<T> withNames(Predicate<? super String> namePredicate) {
|
|
||||||
return new DefaultNamedSetProvider<>(this.getType(), this.getProviders().stream()
|
|
||||||
.map(provider -> (NamedProvider<T>) provider)
|
|
||||||
.filter(namedProvider -> namePredicate.test(namedProvider.getName()))
|
|
||||||
.collect(Collectors.toSet())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NamedProvider<T> withName(String name) {
|
|
||||||
return this.getProviders().stream()
|
|
||||||
.map(provider -> (NamedProvider<T>) provider)
|
|
||||||
.filter(namedProvider -> name.equals(namedProvider.getName()))
|
|
||||||
.findFirst()
|
|
||||||
.orElseThrow(() -> new NullPointerException("There is no NamedProvider present with name " + name));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public NamedSetProvider<T> zipWithNames(NamedSetProvider<? extends T> other) {
|
|
||||||
final Set<NamedProvider<T>> combined = new HashSet<>();
|
|
||||||
this.getProviders().forEach(provider -> combined.add((NamedProvider<T>) provider));
|
|
||||||
other.getProviders().forEach(provider -> combined.add((NamedProvider<T>) provider));
|
|
||||||
return new DefaultNamedSetProvider<>(this.getType(), combined);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
package groowt.util.fp.provider;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class DefaultProvider<T> implements Provider<T> {
|
|
||||||
|
|
||||||
public static <T> Provider<T> of(T t) {
|
|
||||||
Objects.requireNonNull(t);
|
|
||||||
return new DefaultProvider<>(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> Provider<T> ofLazy(Class<T> type, Supplier<? extends T> tSupplier) {
|
|
||||||
Objects.requireNonNull(type);
|
|
||||||
Objects.requireNonNull(tSupplier);
|
|
||||||
return new DefaultProvider<>(type, tSupplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Class<T> type;
|
|
||||||
private final Supplier<T> tSupplier;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
protected DefaultProvider(T t) {
|
|
||||||
this.tSupplier = () -> t;
|
|
||||||
this.type = (Class<T>) t.getClass();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected DefaultProvider(Class<T> type, Supplier<? extends T> tSupplier) {
|
|
||||||
this.type = type;
|
|
||||||
this.tSupplier = tSupplier::get;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<T> getType() {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T get() {
|
|
||||||
return Objects.requireNonNull(this.tSupplier.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
package groowt.util.fp.provider;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class DefaultSetProvider<T> implements SetProvider<T> {
|
|
||||||
|
|
||||||
public static <T> SetProvider<T> ofElements(Class<T> type, Set<? extends T> elements) {
|
|
||||||
return new DefaultSetProvider<>(type, elements.stream()
|
|
||||||
.<Provider<T>>map(DefaultProvider::of)
|
|
||||||
.collect(Collectors.toSet())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> SetProvider<T> ofElementProviders(Class<T> type, Set<? extends Provider<T>> providers) {
|
|
||||||
return new DefaultSetProvider<>(type, providers);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Class<T> type;
|
|
||||||
private final Set<Provider<T>> elementProviders;
|
|
||||||
|
|
||||||
protected DefaultSetProvider(Class<T> type, Set<? extends Provider<T>> elementProviders) {
|
|
||||||
this.type = type;
|
|
||||||
this.elementProviders = new HashSet<>(elementProviders);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<T> getType() {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Provider<T>> getProviders() {
|
|
||||||
return this.elementProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <U> SetProvider<U> map(Class<U> targetType, Function<? super T, ? extends U> mapper) {
|
|
||||||
return new DefaultSetProvider<>(targetType, this.elementProviders.stream()
|
|
||||||
.map(elementProvider -> elementProvider.map(targetType, mapper))
|
|
||||||
.collect(Collectors.toSet())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <U> SetProvider<U> flatMap(Class<U> targetType, Function<? super T, ? extends Provider<U>> mapper) {
|
|
||||||
return new DefaultSetProvider<>(targetType, this.elementProviders.stream()
|
|
||||||
.map(elementProvider -> elementProvider.flatMap(targetType, mapper))
|
|
||||||
.collect(Collectors.toSet())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <U> SetProvider<U> withType(Class<U> desiredType) {
|
|
||||||
return new DefaultSetProvider<>(desiredType, this.elementProviders.stream()
|
|
||||||
.filter(elementProvider -> desiredType.isAssignableFrom(elementProvider.getType()))
|
|
||||||
.map(elementProvider -> elementProvider.map(desiredType, desiredType::cast))
|
|
||||||
.collect(Collectors.toSet())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public SetProvider<T> zip(SetProvider<? extends T> other) {
|
|
||||||
final Set<Provider<T>> combined = new HashSet<>();
|
|
||||||
combined.addAll(this.elementProviders);
|
|
||||||
other.getProviders().forEach(provider -> combined.add((Provider<T>) provider));
|
|
||||||
return new DefaultSetProvider<>(this.getType(), combined);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<T> get() {
|
|
||||||
return this.elementProviders.stream().map(Provider::get).collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package groowt.util.fp.provider;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public interface ListProvider<T> {
|
|
||||||
|
|
||||||
Class<T> getType();
|
|
||||||
|
|
||||||
<U> ListProvider<U> map(Class<U> targetType, Function<? super T, ? extends U> mapper);
|
|
||||||
<U> ListProvider<U> flatMap(Class<U> targetType, Function<? super T, ? extends Provider<? extends U>> flatMapper);
|
|
||||||
|
|
||||||
<U extends T> ListProvider<U> withType(Class<U> desiredType);
|
|
||||||
|
|
||||||
List<T> get();
|
|
||||||
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package groowt.util.fp.provider;
|
|
||||||
|
|
||||||
import groowt.util.fp.hkt.SemiGroup;
|
|
||||||
|
|
||||||
public interface NamedProvider<T> extends Provider<T> {
|
|
||||||
|
|
||||||
String getName();
|
|
||||||
|
|
||||||
default NamedProvider<T> zipWithNames(
|
|
||||||
SemiGroup<T> tSemiGroup,
|
|
||||||
SemiGroup<String> nameSemiGroup,
|
|
||||||
NamedProvider<? extends T> other
|
|
||||||
) {
|
|
||||||
return new DefaultNamedProvider<>(
|
|
||||||
this.getType(),
|
|
||||||
nameSemiGroup.concat(this.getName(), other.getName()),
|
|
||||||
DefaultProvider.ofLazy(this.getType(), () -> tSemiGroup.concat(this.get(), other.get()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package groowt.util.fp.provider;
|
|
||||||
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public interface NamedSetProvider<T> extends SetProvider<T> {
|
|
||||||
|
|
||||||
NamedSetProvider<T> withNames(Predicate<? super String> namePredicate);
|
|
||||||
NamedProvider<T> withName(String name);
|
|
||||||
|
|
||||||
NamedSetProvider<T> zipWithNames(NamedSetProvider<? extends T> other);
|
|
||||||
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
package groowt.util.fp.provider;
|
|
||||||
|
|
||||||
import groowt.util.fp.hkt.SemiGroup;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @implSpec Must throw {@link NullPointerException} if the contained value is {@code null}
|
|
||||||
* when either created or when retrieved.
|
|
||||||
*
|
|
||||||
* @param <T> The type of the value contained within.
|
|
||||||
*/
|
|
||||||
public interface Provider<T> {
|
|
||||||
|
|
||||||
Class<T> getType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @implSpec Must throw {@link NullPointerException} if the value is null.
|
|
||||||
*
|
|
||||||
* @throws NullPointerException if the value contained within this Provider is null.
|
|
||||||
* @return The value.
|
|
||||||
*/
|
|
||||||
T get();
|
|
||||||
|
|
||||||
default T get(Supplier<RuntimeException> onEmpty) {
|
|
||||||
try {
|
|
||||||
return this.get();
|
|
||||||
} catch (NullPointerException nullPointerException) {
|
|
||||||
final RuntimeException onEmptyException = onEmpty.get();
|
|
||||||
onEmptyException.initCause(nullPointerException);
|
|
||||||
throw onEmptyException;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default Provider<T> filter(Predicate<? super T> filter) {
|
|
||||||
Objects.requireNonNull(filter);
|
|
||||||
return new DefaultProvider<>(this.getType(), () -> {
|
|
||||||
final T t = this.get();
|
|
||||||
if (filter.test(t)) {
|
|
||||||
return t;
|
|
||||||
} else {
|
|
||||||
throw new NullPointerException("This Provider is empty (did not pass filter).");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
default <U> Provider<U> map(Class<U> targetType, Function<? super T, ? extends U> mapper) {
|
|
||||||
Objects.requireNonNull(mapper);
|
|
||||||
return new DefaultProvider<>(targetType, () -> mapper.apply(this.get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
default <U> Provider<U> flatMap(
|
|
||||||
Class<U> targetType,
|
|
||||||
Function<? super T, ? extends Provider<? extends U>> flatMapper
|
|
||||||
) {
|
|
||||||
Objects.requireNonNull(flatMapper);
|
|
||||||
return new DefaultProvider<>(targetType, () -> flatMapper.apply(this.get()).get());
|
|
||||||
}
|
|
||||||
|
|
||||||
default Provider<T> zip(
|
|
||||||
SemiGroup<T> semiGroup,
|
|
||||||
Provider<? extends T> other
|
|
||||||
) {
|
|
||||||
Objects.requireNonNull(semiGroup);
|
|
||||||
Objects.requireNonNull(other);
|
|
||||||
return new DefaultProvider<>(this.getType(), () -> semiGroup.concat(this.get(), other.get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package groowt.util.fp.provider;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public interface SetProvider<T> {
|
|
||||||
|
|
||||||
Class<T> getType();
|
|
||||||
|
|
||||||
Set<Provider<T>> getProviders();
|
|
||||||
|
|
||||||
<U> SetProvider<U> map(Class<U> targetType, Function<? super T, ? extends U> mapper);
|
|
||||||
|
|
||||||
<U> SetProvider<U> flatMap(Class<U> targetType, Function<? super T, ? extends Provider<U>> mapper);
|
|
||||||
|
|
||||||
<U> SetProvider<U> withType(Class<U> desiredType);
|
|
||||||
|
|
||||||
Set<T> get();
|
|
||||||
|
|
||||||
SetProvider<T> zip(SetProvider<? extends T> other);
|
|
||||||
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user