diff --git a/util/di/build.gradle b/util/di/build.gradle index dace193..63ae173 100644 --- a/util/di/build.gradle +++ b/util/di/build.gradle @@ -8,6 +8,7 @@ plugins { dependencies { api libs.jakarta.inject + api libs.groovy compileOnlyApi libs.jetbrains.anotations implementation libs.slf4j.api, libs.groovy } diff --git a/util/di/src/main/java/groowt/util/di/AbstractRegistryObjectFactory.java b/util/di/src/main/java/groowt/util/di/AbstractRegistryObjectFactory.java index 70fa706..f6e530e 100644 --- a/util/di/src/main/java/groowt/util/di/AbstractRegistryObjectFactory.java +++ b/util/di/src/main/java/groowt/util/di/AbstractRegistryObjectFactory.java @@ -75,8 +75,8 @@ public abstract class AbstractRegistryObjectFactory extends AbstractInjectingObj } @Override - public void configureRegistry(Consumer use) { - use.accept(this.registry); + public Registry getRegistry() { + return this.registry; } @Override diff --git a/util/di/src/main/java/groowt/util/di/Registry.java b/util/di/src/main/java/groowt/util/di/Registry.java index e7c1862..cdb778e 100644 --- a/util/di/src/main/java/groowt/util/di/Registry.java +++ b/util/di/src/main/java/groowt/util/di/Registry.java @@ -6,6 +6,7 @@ import java.util.function.Consumer; import java.util.function.Predicate; public interface Registry extends ExtensionContainer, QualifierHandlerContainer, ScopeHandlerContainer { + void bind(Class key, Consumer> configure); @Nullable Binding getBinding(Class key); void removeBinding(Class key); diff --git a/util/di/src/main/java/groowt/util/di/RegistryObjectFactory.java b/util/di/src/main/java/groowt/util/di/RegistryObjectFactory.java index a0d236c..d79d7a7 100644 --- a/util/di/src/main/java/groowt/util/di/RegistryObjectFactory.java +++ b/util/di/src/main/java/groowt/util/di/RegistryObjectFactory.java @@ -1,5 +1,9 @@ package groowt.util.di; +import groovy.lang.Closure; +import groovy.lang.DelegatesTo; +import groovy.transform.stc.ClosureParams; +import groovy.transform.stc.FromString; import groowt.util.di.filters.FilterHandler; import groowt.util.di.filters.IterableFilterHandler; import jakarta.inject.Provider; @@ -22,7 +26,21 @@ public interface RegistryObjectFactory extends ObjectFactory { T build(); } - void configureRegistry(Consumer use); + Registry getRegistry(); + + default void configureRegistry(Consumer use) { + use.accept(this.getRegistry()); + } + + default void configureRegistry( + @DelegatesTo(Registry.class) + @ClosureParams(value = FromString.class, options = "groowt.util.di.Registry") + Closure configureClosure + ) { + final Registry registry = this.getRegistry(); + configureClosure.setDelegate(registry); + configureClosure.call(registry); + } @Nullable ScopeHandler findScopeHandler(Class scopeType); diff --git a/util/fp/build.gradle b/util/fp/build.gradle index 97ee98c..02fbfbb 100644 --- a/util/fp/build.gradle +++ b/util/fp/build.gradle @@ -9,6 +9,7 @@ repositories { } dependencies { + api libs.groovy compileOnlyApi libs.jetbrains.anotations } diff --git a/util/fp/src/main/java/groowt/util/fp/hkt/Monoid.java b/util/fp/src/main/java/groowt/util/fp/hkt/Monoid.java index ce5f1cb..fd68ff3 100644 --- a/util/fp/src/main/java/groowt/util/fp/hkt/Monoid.java +++ b/util/fp/src/main/java/groowt/util/fp/hkt/Monoid.java @@ -12,6 +12,14 @@ public final class Monoid { this.zero = Objects.requireNonNull(zero); } + public SemiGroup getSemiGroup() { + return this.semiGroup; + } + + public Zero getZero() { + return this.zero; + } + public T concat(T left, T right) { return this.semiGroup.concat(left, right); } diff --git a/util/fp/src/main/java/groowt/util/fp/property/DefaultListProperty.java b/util/fp/src/main/java/groowt/util/fp/property/DefaultListProperty.java new file mode 100644 index 0000000..5e9cb9a --- /dev/null +++ b/util/fp/src/main/java/groowt/util/fp/property/DefaultListProperty.java @@ -0,0 +1,68 @@ +package groowt.util.fp.property; + +import groowt.util.fp.provider.DefaultListProvider; +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; +import java.util.function.Predicate; + +public class DefaultListProperty implements ListProperty { + + private final List> elementProviders = new ArrayList<>(); + + @Override + public void addElement(T element) { + this.elementProviders.add(Provider.of(element)); + } + + @SuppressWarnings("unchecked") + @Override + public void addProvider(Provider elementProvider) { + this.elementProviders.add((Provider) elementProvider); + } + + @Override + public void addAllElements(Collection elements) { + elements.forEach(this::addElement); + } + + @Override + public void addAllProviders(Collection> elementProviders) { + elementProviders.forEach(this::addProvider); + } + + @Override + public ListProvider mapElements(Function mapper) { + return new DefaultListProvider<>( + this.elementProviders.stream() + .>map(elementProvider -> elementProvider.map(mapper)) + .toList() + ); + } + + @Override + public ListProvider flatMapElements(Function> flatMapper) { + return new DefaultListProvider<>( + this.elementProviders.stream() + .map(elementProvider -> elementProvider.flatMap(flatMapper)) + .toList() + ); + } + + @Override + public ListProvider filterElements(Predicate predicate) { + return new DefaultListProvider<>(this.elementProviders, List.of(predicate)); + } + + @Override + public List get() { + return this.elementProviders.stream() + .map(Provider::get) + .toList(); + } + +} diff --git a/util/fp/src/main/java/groowt/util/fp/property/ListProperty.java b/util/fp/src/main/java/groowt/util/fp/property/ListProperty.java new file mode 100644 index 0000000..9f76bd2 --- /dev/null +++ b/util/fp/src/main/java/groowt/util/fp/property/ListProperty.java @@ -0,0 +1,13 @@ +package groowt.util.fp.property; + +import groowt.util.fp.provider.ListProvider; +import groowt.util.fp.provider.Provider; + +import java.util.Collection; + +public interface ListProperty extends ListProvider { + void addElement(T element); + void addProvider(Provider elementProvider); + void addAllElements(Collection elements); + void addAllProviders(Collection> elementProviders); +} diff --git a/util/fp/src/main/java/groowt/util/fp/property/Property.java b/util/fp/src/main/java/groowt/util/fp/property/Property.java index a173ec6..6b1fbb7 100644 --- a/util/fp/src/main/java/groowt/util/fp/property/Property.java +++ b/util/fp/src/main/java/groowt/util/fp/property/Property.java @@ -1,5 +1,9 @@ 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; @@ -7,7 +11,7 @@ import java.util.function.Supplier; public interface Property extends Provider { - static Property get(Class tClass) { + static Property empty() { return new SimpleProperty<>(); } @@ -17,7 +21,7 @@ public interface Property extends Provider { return property; } - static Property ofLazy(Provider tProvider) { + static Property ofProvider(Provider tProvider) { final Property property = new SimpleProperty<>(); property.set(tProvider); return property; @@ -34,6 +38,12 @@ public interface Property extends Provider { void setConvention(T t); void setConvention(Provider tProvider); + void configure( + @DelegatesTo(type = "T") + @ClosureParams(value = FromString.class, options = "T") + Closure configureClosure + ); + boolean isPresent(); boolean isEmpty(); diff --git a/util/fp/src/main/java/groowt/util/fp/property/SimpleProperty.java b/util/fp/src/main/java/groowt/util/fp/property/SimpleProperty.java index 69260ec..b54680a 100644 --- a/util/fp/src/main/java/groowt/util/fp/property/SimpleProperty.java +++ b/util/fp/src/main/java/groowt/util/fp/property/SimpleProperty.java @@ -1,11 +1,16 @@ package groowt.util.fp.property; +import groovy.lang.Closure; import groowt.util.fp.provider.Provider; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; final class SimpleProperty implements Property { + private final List> configureClosures = new ArrayList<>(); + private Provider provider; private Provider convention; @@ -44,14 +49,30 @@ final class SimpleProperty implements Property { } @Override - public T get() { - if (!this.isPresent()) { - throw new NullPointerException("Cannot get() from an empty Property. Set the value or set the convention."); - } else if (this.provider != null) { - return this.provider.get(); - } else { - return this.convention.get(); + 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; + } + } diff --git a/util/fp/src/main/java/groowt/util/fp/provider/DefaultListProvider.java b/util/fp/src/main/java/groowt/util/fp/provider/DefaultListProvider.java new file mode 100644 index 0000000..a2f6a5f --- /dev/null +++ b/util/fp/src/main/java/groowt/util/fp/provider/DefaultListProvider.java @@ -0,0 +1,68 @@ +package groowt.util.fp.provider; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +public class DefaultListProvider implements ListProvider { + + private final List> elementProviders; + private final List> filters; + + public DefaultListProvider(List> elementProviders) { + this.elementProviders = new ArrayList<>(elementProviders); + this.filters = List.of(); + } + + public DefaultListProvider( + List> elementProviders, + List> filters + ) { + this.elementProviders = new ArrayList<>(elementProviders); + this.filters = new ArrayList<>(filters); + } + + private DefaultListProvider(DefaultListProvider old, Predicate filterToAdd) { + this.elementProviders = new ArrayList<>(old.elementProviders); + this.filters = new ArrayList<>(old.filters); + this.filters.add(filterToAdd); + } + + @Override + public ListProvider filterElements(Predicate predicate) { + return new DefaultListProvider<>(this, predicate); + } + + @Override + public ListProvider mapElements(Function mapper) { + final List> uProviders = this.elementProviders.stream() + .>map(elementProvider -> elementProvider.map(mapper)) + .toList(); + return new DefaultListProvider<>(uProviders); + } + + @Override + public ListProvider flatMapElements(Function> flatMapper) { + final List> uProviders = this.elementProviders.stream() + .map(elementProvider -> elementProvider.flatMap(flatMapper)) + .toList(); + return new DefaultListProvider<>(uProviders); + } + + @Override + public List get() { + final List filtered = this.elementProviders.stream() + .map(Provider::get) + .filter(element -> { + for (final var filter : this.filters) { + if (!filter.test(element)) { + return false; + } + } + return true; + }).toList(); + return new ArrayList<>(filtered); + } + +} diff --git a/util/fp/src/main/java/groowt/util/fp/provider/DefaultNamedProvider.java b/util/fp/src/main/java/groowt/util/fp/provider/DefaultNamedProvider.java new file mode 100644 index 0000000..b35f234 --- /dev/null +++ b/util/fp/src/main/java/groowt/util/fp/provider/DefaultNamedProvider.java @@ -0,0 +1,28 @@ +package groowt.util.fp.provider; + +public class DefaultNamedProvider implements NamedProvider { + + private final String name; + private final Provider delegate; + + public DefaultNamedProvider(String name, T element) { + this.name = name; + this.delegate = Provider.of(element); + } + + public DefaultNamedProvider(String name, Provider delegate) { + this.name = name; + this.delegate = delegate; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public T get() { + return this.delegate.get(); + } + +} diff --git a/util/fp/src/main/java/groowt/util/fp/provider/LazyProvider.java b/util/fp/src/main/java/groowt/util/fp/provider/LazyProvider.java deleted file mode 100644 index 2d0d34a..0000000 --- a/util/fp/src/main/java/groowt/util/fp/provider/LazyProvider.java +++ /dev/null @@ -1,18 +0,0 @@ -package groowt.util.fp.provider; - -import java.util.function.Supplier; - -final class LazyProvider implements Provider { - - private final Supplier lazy; - - public LazyProvider(Supplier supplier) { - this.lazy = supplier; - } - - @Override - public T get() { - return this.lazy.get(); - } - -} diff --git a/util/fp/src/main/java/groowt/util/fp/provider/ListProvider.java b/util/fp/src/main/java/groowt/util/fp/provider/ListProvider.java new file mode 100644 index 0000000..c56a89e --- /dev/null +++ b/util/fp/src/main/java/groowt/util/fp/provider/ListProvider.java @@ -0,0 +1,18 @@ +package groowt.util.fp.provider; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +public interface ListProvider extends Provider> { + + ListProvider mapElements(Function mapper); + ListProvider flatMapElements(Function> flatMapper); + + ListProvider filterElements(Predicate predicate); + + default ListProvider filterElementsByType(Class type) { + return this.filterElements(type::isInstance).mapElements(type::cast); + } + +} diff --git a/util/fp/src/main/java/groowt/util/fp/provider/NamedProvider.java b/util/fp/src/main/java/groowt/util/fp/provider/NamedProvider.java new file mode 100644 index 0000000..f5147fb --- /dev/null +++ b/util/fp/src/main/java/groowt/util/fp/provider/NamedProvider.java @@ -0,0 +1,20 @@ +package groowt.util.fp.provider; + +import groowt.util.fp.hkt.SemiGroup; + +public interface NamedProvider extends Provider { + + String getName(); + + default NamedProvider zipWithNames( + SemiGroup tSemiGroup, + SemiGroup nameSemiGroup, + NamedProvider other + ) { + return new DefaultNamedProvider<>( + nameSemiGroup.concat(this.getName(), other.getName()), + Provider.ofLazy(() -> tSemiGroup.concat(this.get(), other.get())) + ); + } + +} diff --git a/util/fp/src/main/java/groowt/util/fp/provider/Provider.java b/util/fp/src/main/java/groowt/util/fp/provider/Provider.java index dc9ec3a..03a25b3 100644 --- a/util/fp/src/main/java/groowt/util/fp/provider/Provider.java +++ b/util/fp/src/main/java/groowt/util/fp/provider/Provider.java @@ -1,26 +1,65 @@ 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 The type of the value contained within. + */ +@FunctionalInterface public interface Provider { static Provider of(T t) { - return new SimpleProvider<>(t); + Objects.requireNonNull(t); + return () -> t; } static Provider ofLazy(Supplier tSupplier) { - return new LazyProvider<>(tSupplier); + Objects.requireNonNull(tSupplier); + return () -> Objects.requireNonNull(tSupplier.get(), "This Provider is empty."); } + /** + * @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 Provider filter(Predicate filter) { + Objects.requireNonNull(filter); + return () -> { + final T t = this.get(); + if (filter.test(t)) { + return t; + } else { + throw new NullPointerException("This Provider is empty."); + } + }; + } + default Provider map(Function mapper) { - return new LazyProvider<>(() -> mapper.apply(this.get())); + Objects.requireNonNull(mapper); + return () -> mapper.apply(this.get()); } default Provider flatMap(Function> flatMapper) { - return new LazyProvider<>(() -> flatMapper.apply(this.get()).get()); + Objects.requireNonNull(flatMapper); + return () -> flatMapper.apply(this.get()).get(); + } + + default Provider zip(SemiGroup semiGroup, Provider other) { + Objects.requireNonNull(semiGroup); + Objects.requireNonNull(other); + return () -> semiGroup.concat(this.get(), other.get()); } } diff --git a/util/fp/src/main/java/groowt/util/fp/provider/SimpleProvider.java b/util/fp/src/main/java/groowt/util/fp/provider/SimpleProvider.java deleted file mode 100644 index 2220b1b..0000000 --- a/util/fp/src/main/java/groowt/util/fp/provider/SimpleProvider.java +++ /dev/null @@ -1,18 +0,0 @@ -package groowt.util.fp.provider; - -import java.util.Objects; - -final class SimpleProvider implements Provider { - - private final T t; - - public SimpleProvider(T t) { - this.t = Objects.requireNonNull(t); - } - - @Override - public T get() { - return t; - } - -}