commit da6eaff6e7531d67326c5b61f256b4c9bcbeeadc Author: JFronny Date: Thu Apr 28 20:52:32 2022 +0200 0.1.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd00d92 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..2ae27dc --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,41 @@ +image: gradle:alpine + +variables: + GRADLE_OPTS: "-Dorg.gradle.daemon=false" + +before_script: + - GRADLE_USER_HOME="$(pwd)/.gradle" + - export GRADLE_USER_HOME + +stages: + - build + - test + - deploy + +build: + stage: build + script: gradle --build-cache assemble + cache: + key: "$CI_COMMIT_REF_NAME" + policy: push + paths: + - build + - .gradle + +test: + stage: test + script: gradle check + cache: + key: "$CI_COMMIT_REF_NAME" + policy: pull + paths: + - build + - .gradle + +deploy: + only: + refs: + - master + stage: deploy + script: + - gradle --build-cache publish -Pmaven="$CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/maven" diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..464ed6b --- /dev/null +++ b/build.gradle @@ -0,0 +1,52 @@ +plugins { + id 'java-library' + id 'maven-publish' + +} + +repositories { + mavenCentral() +} + +dependencies { + compileOnly 'org.jetbrains:annotations:23.0.0' + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' +} + +test { + useJUnitPlatform() +} + +publishing { + publications { + mavenJava(MavenPublication) { + groupId = 'io.gitlab.jfronny' + artifactId = 'commons' + var cl = Calendar.instance + var day = "${cl.get(cl.YEAR)}.${cl.get(cl.MONTH) + 1}.${cl.get(cl.DATE)}" + var time = "${cl.get(cl.HOUR_OF_DAY)}.${cl.get(cl.MINUTE)}.${cl.get(cl.SECOND)}" + version = "0.1.0.$day.$time" + + from components.java + } + } + + def ENV = System.getenv() + if (project.hasProperty("maven")) { + repositories.maven { + url = project.getProperty("maven") + name = "dynamic" + + credentials(HttpHeaderCredentials) { + name = "Job-Token" + value = ENV.CI_JOB_TOKEN + } + authentication { + header(HttpHeaderAuthentication) + } + } + } + repositories.mavenLocal() +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..51c15da --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'Commons' + diff --git a/src/main/java/io/gitlab/jfronny/commons/LazySupplier.java b/src/main/java/io/gitlab/jfronny/commons/LazySupplier.java new file mode 100644 index 0000000..143ce66 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/LazySupplier.java @@ -0,0 +1,53 @@ +package io.gitlab.jfronny.commons; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * A supplier that proxies to a separate supplier and caches its result + */ +public class LazySupplier implements Supplier { + private final Supplier supplier; + private T cache = null; + + /** + * Create a lazy supplier from a pre-initialized value. + * The backing supplier will be empty and never called. + * @param value The value to initialize the cache with + */ + public LazySupplier(@NotNull T value) { + this.supplier = () -> { + throw new RuntimeException("Supplier should have never been called"); + }; + cache = value; + } + + /** + * Create a lazy supplier backed with the specified supplier. + * It will at most be called once when this supplier is first used. + * @param supplier The backing supplier + */ + public LazySupplier(@NotNull Supplier supplier) { + this.supplier = Objects.requireNonNull(supplier); + } + + @Override @Contract(pure = true) @NotNull public T get() { + if (cache == null) cache = supplier.get(); + return cache; + } + + /** + * Generates a new lazy supplier from a function. + * This function is provided with this lazy supplier as a data source for transformation. + * Allows complex transformations to not be run if a later supplier has a shortcut + * @param after A backing function for the new lazy supplier + * @return A new lazy supplier + */ + @Contract(pure = true) @NotNull public LazySupplier andThen(@NotNull Function, T> after) { + return new LazySupplier<>(() -> after.apply(this)); + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/throwable/Coerce.java b/src/main/java/io/gitlab/jfronny/commons/throwable/Coerce.java new file mode 100644 index 0000000..ddc7354 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/throwable/Coerce.java @@ -0,0 +1,117 @@ +package io.gitlab.jfronny.commons.throwable; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +/** + * Removes the need for casting when composing throwables + */ +public class Coerce { + /** + * Coerce the java compiler to interpret a lambda as a throwing biConsumer + * @param tr A throwing biConsumer + * @return The throwing biConsumer + */ + @Contract(pure = true) @NotNull + public static ThrowingBiConsumer biConsumer(@NotNull ThrowingBiConsumer tr) { + return tr; + } + + /** + * Coerce the java compiler to interpret a lambda as a throwing biFunction + * @param tr A throwing biFunction + * @return The throwing biFunction + */ + @Contract(pure = true) @NotNull + public static ThrowingBiFunction biFunction(@NotNull ThrowingBiFunction tr) { + return tr; + } + + /** + * Coerce the java compiler to interpret a lambda as a throwing booleanSupplier + * @param tr A throwing booleanSupplier + * @return The throwing booleanSupplier + */ + @Contract(pure = true) @NotNull + public static ThrowingBooleanSupplier booleanSupplier(@NotNull ThrowingBooleanSupplier tr) { + return tr; + } + + /** + * Coerce the java compiler to interpret a lambda as a throwing consumer + * @param tr A throwing consumer + * @return The throwing consumer + */ + @Contract(pure = true) @NotNull + public static ThrowingConsumer consumer(@NotNull ThrowingConsumer tr) { + return tr; + } + + /** + * Coerce the java compiler to interpret a lambda as a throwing function + * @param tr A throwing function + * @return The throwing function + */ + @Contract(pure = true) @NotNull + public static ThrowingFunction function(@NotNull ThrowingFunction tr) { + return tr; + } + + /** + * Coerce the java compiler to interpret a lambda as a throwing predicate + * @param tr A throwing predicate + * @return The throwing predicate + */ + @Contract(pure = true) @NotNull + public static ThrowingPredicate predicate(@NotNull ThrowingPredicate tr) { + return tr; + } + + /** + * Coerce the java compiler to interpret a lambda as a throwing runnable + * @param tr A throwing runnable + * @return The throwing runnable + */ + @Contract(pure = true) @NotNull + public static ThrowingRunnable runnable(@NotNull ThrowingRunnable tr) { + return tr; + } + + /** + * Coerce the java compiler to interpret a lambda as a throwing supplier + * @param tr A throwing supplier + * @return The throwing supplier + */ + @Contract(pure = true) @NotNull + public static ThrowingSupplier supplier(@NotNull ThrowingSupplier tr) { + return tr; + } + + /** + * Example: + * {@code interface Test { + * T get(); + * void consume(T t); + * } + * class Example { + * Test instance; + * void function() { + * Coerce.pin(instance.get(), v -> instance.consume(v)); // This can compile + * instance.consume(instance.get()); // This cannot + * } + * }} + * @param value The value to pin + * @param func The function to apply to the pinned value + * @param The type of the value to pin + * @param The return type of the function + * @param An exception type (if needed) + * @return The result of the function + * @throws TEx If the function throws, nothing is handled here + */ + public static TOut pin(@Nullable TIn value, @NotNull ThrowingFunction func) throws TEx { + return Objects.requireNonNull(func).apply(value); + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBiConsumer.java b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBiConsumer.java new file mode 100644 index 0000000..5187ff7 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBiConsumer.java @@ -0,0 +1,101 @@ +package io.gitlab.jfronny.commons.throwable; + +import io.gitlab.jfronny.commons.tuple.Tuple; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +@FunctionalInterface +public interface ThrowingBiConsumer { + void accept(T var1, U var2) throws TEx; + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingConsumer, TEx> fromTuple() { + return (t) -> this.accept(t.left(), t.right()); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingConsumer compose(@NotNull ThrowingFunction left, @NotNull ThrowingFunction right) { + Objects.requireNonNull(left); + Objects.requireNonNull(right); + return (t) -> this.accept(left.apply(t), right.apply(t)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBiConsumer biCompose(@NotNull ThrowingFunction left, @NotNull ThrowingFunction right) { + Objects.requireNonNull(left); + Objects.requireNonNull(right); + return (l, r) -> this.accept(left.apply(l), right.apply(r)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBiConsumer composeLeft(@NotNull ThrowingFunction before) { + Objects.requireNonNull(before); + return (l, r) -> this.accept(before.apply(l), r); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBiConsumer composeRight(@NotNull ThrowingFunction before) { + Objects.requireNonNull(before); + return (l, r) -> this.accept(l, before.apply(r)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBiConsumer andThen(@NotNull ThrowingBiConsumer after) { + Objects.requireNonNull(after); + return (l, r) -> { + this.accept(l, r); + after.accept(l, r); + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBiConsumer andThen(@NotNull ThrowingRunnable after) { + Objects.requireNonNull(after); + return (l, r) -> { + this.accept(l, r); + after.run(); + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default BiConsumer addHandler(@NotNull Consumer handler) { + Objects.requireNonNull(handler); + return (l, r) -> { + try { + this.accept(l, r); + } catch (Throwable e) { + handler.accept(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default BiConsumer addHandler(@NotNull Class exception, @NotNull Consumer handler) { + Objects.requireNonNull(handler); + return (l, r) -> { + try { + this.accept(l, r); + } catch (Throwable e) { + if (exception.isAssignableFrom(e.getClass())) + handler.accept((TEx) e); + else throw Try.runtimeException(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default BiConsumer orThrow() { + return (l, r) -> { + try { + this.accept(l, r); + } catch (Throwable e) { + throw Try.runtimeException(e); + } + }; + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBiFunction.java b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBiFunction.java new file mode 100644 index 0000000..8e9e9ac --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBiFunction.java @@ -0,0 +1,95 @@ +package io.gitlab.jfronny.commons.throwable; + +import io.gitlab.jfronny.commons.tuple.Tuple; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.Function; + +@FunctionalInterface +public interface ThrowingBiFunction { + R apply(T var1, U var2) throws TEx; + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingFunction, R, TEx> fromTuple() { + return (t) -> this.apply(t.left(), t.right()); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingFunction compose(@NotNull ThrowingFunction left, @NotNull ThrowingFunction right) { + Objects.requireNonNull(left); + Objects.requireNonNull(right); + return (t) -> this.apply(left.apply(t), right.apply(t)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBiFunction biCompose(@NotNull ThrowingFunction left, @NotNull ThrowingFunction right) { + Objects.requireNonNull(left); + Objects.requireNonNull(right); + return (l, r) -> this.apply(left.apply(l), right.apply(r)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBiFunction composeLeft(@NotNull ThrowingFunction before) { + Objects.requireNonNull(before); + return (l, r) -> this.apply(before.apply(l), r); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBiFunction composeRight(@NotNull ThrowingFunction before) { + Objects.requireNonNull(before); + return (l, r) -> this.apply(l, before.apply(r)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBiFunction andThen(@NotNull ThrowingFunction after) { + Objects.requireNonNull(after); + return (t, u) -> after.apply(this.apply(t, u)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBiConsumer andThen(@NotNull ThrowingConsumer after) { + Objects.requireNonNull(after); + return (t, u) -> after.accept(this.apply(t, u)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default BiFunction addHandler(@NotNull Function handler) { + Objects.requireNonNull(handler); + return (t, u) -> { + try { + return this.apply(t, u); + } catch (Throwable e) { + return handler.apply(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default BiFunction addHandler(@NotNull Class exception, @NotNull Function handler) { + Objects.requireNonNull(handler); + return (t, u) -> { + try { + return this.apply(t, u); + } catch (Throwable e) { + if (exception.isAssignableFrom(e.getClass())) + return handler.apply((TEx) e); + else throw Try.runtimeException(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default BiFunction orThrow() { + return (t, u) -> { + try { + return this.apply(t, u); + } catch (Throwable e) { + throw Try.runtimeException(e); + } + }; + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBooleanSupplier.java b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBooleanSupplier.java new file mode 100644 index 0000000..583219f --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingBooleanSupplier.java @@ -0,0 +1,97 @@ +package io.gitlab.jfronny.commons.throwable; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.function.BooleanSupplier; +import java.util.function.Predicate; + +@FunctionalInterface +public interface ThrowingBooleanSupplier { + boolean get() throws TEx; + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingPredicate and(@NotNull ThrowingPredicate other) { + Objects.requireNonNull(other); + return (t) -> this.get() && other.test(t); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBooleanSupplier and(@NotNull ThrowingBooleanSupplier other) { + Objects.requireNonNull(other); + return () -> this.get() && other.get(); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingPredicate or(@NotNull ThrowingPredicate other) { + Objects.requireNonNull(other); + return (t) -> this.get() || other.test(t); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBooleanSupplier or(@NotNull ThrowingBooleanSupplier other) { + Objects.requireNonNull(other); + return () -> this.get() || other.get(); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingPredicate xor(@NotNull ThrowingPredicate other) { + Objects.requireNonNull(other); + return (t) -> this.get() ^ other.test(t); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBooleanSupplier xor(@NotNull ThrowingBooleanSupplier other) { + Objects.requireNonNull(other); + return () -> this.get() ^ other.get(); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBooleanSupplier negate() { + return () -> !this.get(); + } + + @Contract(pure = true) @NotNull + static ThrowingBooleanSupplier not(@NotNull ThrowingBooleanSupplier target) { + return Objects.requireNonNull(target).negate(); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default BooleanSupplier addHandler(@NotNull Predicate handler) { + Objects.requireNonNull(handler); + return () -> { + try { + return this.get(); + } catch (Throwable e) { + return handler.test(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default BooleanSupplier addHandler(@NotNull Class exception, @NotNull Predicate handler) { + Objects.requireNonNull(handler); + return () -> { + try { + return this.get(); + } catch (Throwable e) { + if (exception.isAssignableFrom(e.getClass())) + return handler.test((TEx) e); + else throw Try.runtimeException(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default BooleanSupplier orThrow() { + return () -> { + try { + return this.get(); + } catch (Throwable e) { + throw Try.runtimeException(e); + } + }; + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingConsumer.java b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingConsumer.java new file mode 100644 index 0000000..6ee6430 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingConsumer.java @@ -0,0 +1,89 @@ +package io.gitlab.jfronny.commons.throwable; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.function.Consumer; + +@FunctionalInterface +public interface ThrowingConsumer { + void accept(T var1) throws TEx; + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingConsumer compose(@NotNull ThrowingFunction before) { + Objects.requireNonNull(before); + return (t) -> this.accept(before.apply(t)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingRunnable compose(@NotNull ThrowingSupplier before) { + Objects.requireNonNull(before); + return () -> this.accept(before.get()); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingConsumer andThen(@NotNull ThrowingConsumer after) { + Objects.requireNonNull(after); + return (t) -> { + this.accept(t); + after.accept(t); + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingConsumer andThen(@NotNull ThrowingRunnable after) { + Objects.requireNonNull(after); + return (t) -> { + this.accept(t); + after.run(); + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBiConsumer compound(@NotNull ThrowingConsumer other) { + Objects.requireNonNull(other); + return (l, r) -> { + this.accept(l); + other.accept(r); + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Consumer addHandler(@NotNull Consumer handler) { + Objects.requireNonNull(handler); + return (t) -> { + try { + this.accept(t); + } catch (Throwable e) { + handler.accept(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Consumer addHandler(@NotNull Class exception, @NotNull Consumer handler) { + Objects.requireNonNull(handler); + return (t) -> { + try { + this.accept(t); + } catch (Throwable e) { + if (exception.isAssignableFrom(e.getClass())) + handler.accept((TEx) e); + else throw Try.runtimeException(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Consumer orThrow() { + return (t) -> { + try { + this.accept(t); + } catch (Throwable e) { + throw Try.runtimeException(e); + } + }; + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingFunction.java b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingFunction.java new file mode 100644 index 0000000..e368a5a --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingFunction.java @@ -0,0 +1,74 @@ +package io.gitlab.jfronny.commons.throwable; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.function.Function; + +@FunctionalInterface +public interface ThrowingFunction { + R apply(T var1) throws TEx; + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingSupplier compose(@NotNull ThrowingSupplier before) { + Objects.requireNonNull(before); + return () -> this.apply(before.get()); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingFunction compose(@NotNull ThrowingFunction before) { + Objects.requireNonNull(before); + return (v) -> this.apply(before.apply(v)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingFunction andThen(@NotNull ThrowingFunction after) { + Objects.requireNonNull(after); + return (t) -> after.apply(this.apply(t)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingConsumer andThen(@NotNull ThrowingConsumer after) { + Objects.requireNonNull(after); + return (t) -> after.accept(this.apply(t)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Function addHandler(@NotNull Function handler) { + Objects.requireNonNull(handler); + return (t) -> { + try { + return this.apply(t); + } catch (Throwable e) { + return handler.apply(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Function addHandler(@NotNull Class exception, Function handler) { + Objects.requireNonNull(handler); + return (t) -> { + try { + return this.apply(t); + } catch (Throwable e) { + if (exception.isAssignableFrom(e.getClass())) + return handler.apply((TEx) e); + else throw Try.runtimeException(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Function orThrow() { + return (t) -> { + try { + return this.apply(t); + } catch (Throwable e) { + throw Try.runtimeException(e); + } + }; + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingPredicate.java b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingPredicate.java new file mode 100644 index 0000000..c499d75 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingPredicate.java @@ -0,0 +1,114 @@ +package io.gitlab.jfronny.commons.throwable; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.Predicate; + +@FunctionalInterface +public interface ThrowingPredicate { + boolean test(T var1) throws TEx; + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingPredicate compose(@NotNull ThrowingFunction before) { + Objects.requireNonNull(before); + return (t) -> this.test(before.apply(t)); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingBooleanSupplier compose(@NotNull ThrowingSupplier before) { + Objects.requireNonNull(before); + return () -> this.test(before.get()); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingPredicate and(@NotNull ThrowingPredicate other) { + Objects.requireNonNull(other); + return (t) -> this.test(t) && other.test(t); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingPredicate and(@NotNull ThrowingBooleanSupplier other) { + Objects.requireNonNull(other); + return (t) -> this.test(t) && other.get(); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingPredicate or(@NotNull ThrowingPredicate other) { + Objects.requireNonNull(other); + return (t) -> this.test(t) || other.test(t); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingPredicate or(@NotNull ThrowingBooleanSupplier other) { + Objects.requireNonNull(other); + return (t) -> this.test(t) || other.get(); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingPredicate xor(@NotNull ThrowingPredicate other) { + Objects.requireNonNull(other); + return (t) -> this.test(t) ^ other.test(t); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingPredicate xor(@NotNull ThrowingBooleanSupplier other) { + Objects.requireNonNull(other); + return (t) -> this.test(t) ^ other.get(); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingPredicate negate() { + return (t) -> !this.test(t); + } + + @Contract(pure = true) @NotNull + static ThrowingPredicate isEqual(@Nullable Object targetRef) { + return null == targetRef ? Objects::isNull : targetRef::equals; + } + + @Contract(pure = true) @NotNull + static ThrowingPredicate not(@NotNull ThrowingPredicate target) { + return Objects.requireNonNull(target).negate(); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Predicate addHandler(@NotNull Predicate handler) { + Objects.requireNonNull(handler); + return (r) -> { + try { + return this.test(r); + } catch (Throwable e) { + return handler.test(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Predicate addHandler(@NotNull Class exception, @NotNull Predicate handler) { + Objects.requireNonNull(handler); + return (r) -> { + try { + return this.test(r); + } catch (Throwable e) { + if (exception.isAssignableFrom(e.getClass())) + return handler.test((TEx) e); + else throw Try.runtimeException(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Predicate orThrow() { + return (r) -> { + try { + return this.test(r); + } catch (Throwable e) { + throw Try.runtimeException(e); + } + }; + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingRunnable.java b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingRunnable.java new file mode 100644 index 0000000..efc5909 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingRunnable.java @@ -0,0 +1,68 @@ +package io.gitlab.jfronny.commons.throwable; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.function.Consumer; + +@FunctionalInterface +public interface ThrowingRunnable { + void run() throws TEx; + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingRunnable compose(@NotNull ThrowingRunnable before) { + Objects.requireNonNull(before); + return () -> { + before.run(); + this.run(); + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingRunnable andThen(@NotNull ThrowingRunnable after) { + Objects.requireNonNull(after); + return () -> { + this.run(); + after.run(); + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Runnable addHandler(@NotNull Consumer handler) { + Objects.requireNonNull(handler); + return () -> { + try { + this.run(); + } catch (Throwable e) { + handler.accept(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Runnable addHandler(@NotNull Class exception, @NotNull Consumer handler) { + Objects.requireNonNull(handler); + return () -> { + try { + this.run(); + } catch (Throwable e) { + if (exception.isAssignableFrom(e.getClass())) + handler.accept((TEx) e); + else throw Try.runtimeException(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Runnable orThrow() { + return () -> { + try { + this.run(); + } catch (Throwable e) { + throw Try.runtimeException(e); + } + }; + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingSupplier.java b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingSupplier.java new file mode 100644 index 0000000..87f5490 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/throwable/ThrowingSupplier.java @@ -0,0 +1,63 @@ +package io.gitlab.jfronny.commons.throwable; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; + +@FunctionalInterface +public interface ThrowingSupplier { + T get() throws TEx; + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingSupplier andThen(@NotNull ThrowingFunction after) { + Objects.requireNonNull(after); + return () -> after.apply(this.get()); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default ThrowingRunnable andThen(@NotNull ThrowingConsumer after) { + Objects.requireNonNull(after); + return () -> after.accept(this.get()); + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Supplier addHandler(@NotNull Function handler) { + Objects.requireNonNull(handler); + return () -> { + try { + return this.get(); + } catch (Throwable e) { + return handler.apply(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Supplier addHandler(@NotNull Class exception, @NotNull Function handler) { + Objects.requireNonNull(handler); + return () -> { + try { + return this.get(); + } catch (Throwable e) { + if (exception.isAssignableFrom(e.getClass())) + return handler.apply((TEx) e); + else throw Try.runtimeException(e); + } + }; + } + + @Contract(pure = true) @NotNull @ApiStatus.NonExtendable + default Supplier orThrow() { + return () -> { + try { + return this.get(); + } catch (Throwable e) { + throw Try.runtimeException(e); + } + }; + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/throwable/Try.java b/src/main/java/io/gitlab/jfronny/commons/throwable/Try.java new file mode 100644 index 0000000..24ae0c5 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/throwable/Try.java @@ -0,0 +1,128 @@ +package io.gitlab.jfronny.commons.throwable; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.*; + +/** + * try-catch without the hassle (intended for field declarations or lambdas) + */ +public class Try { + /** + * Run a method or an exception handler + * @param tr The method that should be run + * @param alternative A method to run if tr fails + */ + @Contract(pure = true) + public static void orElse(@NotNull ThrowingRunnable tr, @NotNull Consumer alternative) { + Objects.requireNonNull(tr).addHandler(alternative).run(); + } + + /** + * Run a method or an exception handler + * @param tr The method that should be run + * @param alternative A method to run if tr fails, should return a default value + */ + @Contract(pure = true) @NotNull + public static T orElse(@NotNull ThrowingSupplier tr, @NotNull Function alternative) { + return Objects.requireNonNull(tr).addHandler(alternative).get(); + } + + /** + * Run a method or throw a runtime exception (aka just ignore that it might throw something) + * @param tr The method that should be run + */ + @Contract(pure = true) + public static void orThrow(@NotNull ThrowingRunnable tr) { + Objects.requireNonNull(tr).orThrow().run(); + } + + /** + * Run a method or throw a runtime exception (aka just ignore that it might throw something) + * @param tr The method that should be run + */ + @Contract(pure = true) @NotNull + public static T orThrow(@NotNull ThrowingSupplier tr) { + return Objects.requireNonNull(tr).orThrow().get(); + } + + /** + * Converts a throwing BiConsumer to a normal one by adding an exception handler + * @param tr The method that should be run + * @param handler A method to run if tr fails + * @return A normal BiConsumer + */ + @Contract(pure = true) @NotNull + public static BiConsumer handle(@NotNull ThrowingBiConsumer tr, @NotNull Consumer handler) { + return Objects.requireNonNull(tr).addHandler(handler); + } + + /** + * Converts a throwing BiFunction to a normal one by adding an exception handler + * @param tr The method that should be run + * @param handler A method to run if tr fails, should return a default value + * @return A normal BiFunction + */ + @Contract(pure = true) @NotNull + public static BiFunction handle(@NotNull ThrowingBiFunction tr, @NotNull Function handler) { + return Objects.requireNonNull(tr).addHandler(handler); + } + + /** + * Converts a throwing Consumer to a normal one by adding an exception handler + * @param tr The method that should be run + * @param handler A method to run if tr fails + * @return A normal Consumer + */ + @Contract(pure = true) @NotNull + public static Consumer handle(@NotNull ThrowingConsumer tr, @NotNull Consumer handler) { + return Objects.requireNonNull(tr).addHandler(handler); + } + + /** + * Converts a throwing Function to a normal one by adding an exception handler + * @param tr The method that should be run + * @param handler A method to run if tr fails, should return a default value + * @return A normal Function + */ + @Contract(pure = true) @NotNull + public static Function handle(@NotNull ThrowingFunction tr, @NotNull Function handler) { + return Objects.requireNonNull(tr).addHandler(handler); + } + + /** + * Converts a throwing Runnable to a normal one by adding an exception handler + * @param tr The method that should be run + * @param handler A method to run if tr fails + * @return A normal Runnable + */ + @Contract(pure = true) @NotNull + public static Runnable handle(@NotNull ThrowingRunnable tr, @NotNull Consumer handler) { + return Objects.requireNonNull(tr).addHandler(handler); + } + + /** + * Converts a throwing Supplier to a normal one by adding an exception handler + * @param tr The method that should be run + * @param handler A method to run if tr fails, should return a default value + * @return A normal Supplier + */ + @Contract(pure = true) @NotNull + public static Supplier handle(@NotNull ThrowingSupplier tr, @NotNull Function handler) { + return Objects.requireNonNull(tr).addHandler(handler); + } + + /** + * Converts any throwable to a RuntimeException (returns it if it is already one) + * @param t A throwable to convert + * @return A runtime exception + */ + @Contract(pure = true) @NotNull + protected static RuntimeException runtimeException(@NotNull Throwable t) { + Objects.requireNonNull(t); + return t instanceof RuntimeException e ? e : new RuntimeException(t); + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/tuple/Quadruple.java b/src/main/java/io/gitlab/jfronny/commons/tuple/Quadruple.java new file mode 100644 index 0000000..695e33b --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/tuple/Quadruple.java @@ -0,0 +1,45 @@ +package io.gitlab.jfronny.commons.tuple; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.Function; + +public record Quadruple(@Nullable T1 val1, @Nullable T2 val2, @Nullable T3 val3, @Nullable T4 val4) { + @Contract(pure = true) @NotNull + public static Quadruple of(@Nullable T1 val1, @Nullable T2 val2, @Nullable T3 val3, @Nullable T4 val4) { + return new Quadruple<>(val1, val2, val3, val4); + } + + @Contract(pure = true) @NotNull + public static Quadruple of(@NotNull Tuple left, @NotNull Tuple right) { + return new Quadruple<>(left.left(), left.right(), right.left(), right.right()); + } + + @Contract(pure = true) @NotNull + public Tuple, Tuple> slice() { + return new Tuple<>(new Tuple<>(val1, val2), new Tuple<>(val3, val4)); + } + + @Contract(pure = true) @NotNull + public Quadruple map1(@NotNull Function mapper) { + return new Quadruple<>(Objects.requireNonNull(mapper).apply(val1), val2, val3, val4); + } + + @Contract(pure = true) @NotNull + public Quadruple map2(@NotNull Function mapper) { + return new Quadruple<>(val1, Objects.requireNonNull(mapper).apply(val2), val3, val4); + } + + @Contract(pure = true) @NotNull + public Quadruple map3(@NotNull Function mapper) { + return new Quadruple<>(val1, val2, Objects.requireNonNull(mapper).apply(val3), val4); + } + + @Contract(pure = true) @NotNull + public Quadruple map4(@NotNull Function mapper) { + return new Quadruple<>(val1, val2, val3, Objects.requireNonNull(mapper).apply(val4)); + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/tuple/Single.java b/src/main/java/io/gitlab/jfronny/commons/tuple/Single.java new file mode 100644 index 0000000..c2aea21 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/tuple/Single.java @@ -0,0 +1,52 @@ +package io.gitlab.jfronny.commons.tuple; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +public record Single(@Nullable T1 val) { + @Contract(pure = true) @NotNull + public static Single of(@Nullable T1 val) { + return new Single<>(val); + } + + @Contract(pure = true) + public boolean isNull() { + return val == null; + } + + @Contract(pure = true) + public boolean isPresent() { + return val != null; + } + + @Contract(pure = true) @NotNull + public Predicate asEqualsPredicate() { + return val == null ? Objects::isNull : val::equals; + } + + @Contract(pure = true) @NotNull + public Predicate asInstanceEqualsPredicate() { + return v -> v == val; + } + + @Contract(pure = true) @NotNull + public Supplier asSupplier() { + return () -> val; + } + + @Contract(pure = true) @NotNull + public Single map(@NotNull Function mapper) { + return new Single<>(Objects.requireNonNull(mapper).apply(val)); + } + + @Contract(pure = true) @Nullable + public T1 get() { + return val; + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/tuple/Triple.java b/src/main/java/io/gitlab/jfronny/commons/tuple/Triple.java new file mode 100644 index 0000000..b2141ca --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/tuple/Triple.java @@ -0,0 +1,55 @@ +package io.gitlab.jfronny.commons.tuple; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.Function; + +public record Triple(@Nullable T1 val1, @Nullable T2 val2, @Nullable T3 val3) { + @Contract(pure = true) @NotNull + public static Triple of(@Nullable T1 val1, @Nullable T2 val2, @Nullable T3 val3) { + return new Triple<>(val1, val2, val3); + } + + @Contract(pure = true) @NotNull + public Triple map1(@NotNull Function mapper) { + return new Triple<>(Objects.requireNonNull(mapper).apply(val1), val2, val3); + } + + @Contract(pure = true) @NotNull + public Triple map2(@NotNull Function mapper) { + return new Triple<>(val1, Objects.requireNonNull(mapper).apply(val2), val3); + } + + @Contract(pure = true) @NotNull + public Triple map3(@NotNull Function mapper) { + return new Triple<>(val1, val2, Objects.requireNonNull(mapper).apply(val3)); + } + + @Contract(pure = true) @NotNull + public Triple lShift() { + return new Triple<>(val2, val3, val1); + } + + @Contract(pure = true) @NotNull + public Triple rShift() { + return new Triple<>(val3, val1, val2); + } + + @Contract(pure = true) @NotNull + public Triple swapL() { + return new Triple<>(val2, val1, val3); + } + + @Contract(pure = true) @NotNull + public Triple swapR() { + return new Triple<>(val1, val3, val2); + } + + @Contract(pure = true) @NotNull + public Triple reverse() { + return new Triple<>(val3, val2, val1); + } +} diff --git a/src/main/java/io/gitlab/jfronny/commons/tuple/Tuple.java b/src/main/java/io/gitlab/jfronny/commons/tuple/Tuple.java new file mode 100644 index 0000000..ebd65ff --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/commons/tuple/Tuple.java @@ -0,0 +1,93 @@ +package io.gitlab.jfronny.commons.tuple; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Function; + +public record Tuple(@Nullable T1 left, @Nullable T2 right) { + @Contract(pure = true) @NotNull + public static Tuple of(@Nullable T1 left, @Nullable T2 right) { + return new Tuple<>(left, right); + } + + @Contract(pure = true) @NotNull + public static Tuple from(@NotNull Map.Entry entry) { + Objects.requireNonNull(entry); + return new Tuple<>(entry.getKey(), entry.getValue()); + } + + @Contract(pure = true) @NotNull + public Tuple mapLeft(@NotNull Function mapper) { + return new Tuple<>(Objects.requireNonNull(mapper).apply(left), right); + } + + @Contract(pure = true) @NotNull + public Tuple mapRight(@NotNull Function mapper) { + return new Tuple<>(left, Objects.requireNonNull(mapper).apply(right)); + } + + @Contract(pure = true) @NotNull + public Tuple swap() { + return new Tuple<>(right, left); + } + + @Contract(pure = true) + public static Set> from(@NotNull Map map) { + Objects.requireNonNull(map); + return new AbstractSet<>() { + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public boolean contains(Object o) { + if (o instanceof Tuple tup) { + if (map.containsKey(tup.left) && map.get(tup.left) == tup.right) + return true; + } + if (map.containsKey(o)) + return true; + return false; + } + + @NotNull + @Override + public Iterator> iterator() { + return map.entrySet().stream().map(Tuple::from).iterator(); + } + + @NotNull + @Override + public Object[] toArray() { + return map.entrySet().stream().map(Tuple::from).toArray(); + } + + @Override + public boolean add(Tuple t1T2Tuple) { + return map.put(t1T2Tuple.left, t1T2Tuple.right) == null; + } + + @Override + public boolean remove(Object o) { + if (o instanceof Tuple t) { + return map.remove(t.left, t.right); + } + return map.remove(o) != null; + } + + @Override + public void clear() { + map.clear(); + } + }; + } +} diff --git a/src/test/java/io/gitlab/jfronny/commons/test/ThrowableTest.java b/src/test/java/io/gitlab/jfronny/commons/test/ThrowableTest.java new file mode 100644 index 0000000..b6a8a41 --- /dev/null +++ b/src/test/java/io/gitlab/jfronny/commons/test/ThrowableTest.java @@ -0,0 +1,63 @@ +package io.gitlab.jfronny.commons.test; + +import io.gitlab.jfronny.commons.throwable.ThrowingConsumer; +import io.gitlab.jfronny.commons.throwable.ThrowingSupplier; +import io.gitlab.jfronny.commons.throwable.Try; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.*; + +public class ThrowableTest { + private static final String NORMAL = "Hello, World!"; + private static final String ALT = "Alt"; + + @Test + void tryOrElse() { + assertDoesNotThrow(() -> Try.orElse(this::throwNoReturn, this::noop)); + assertEquals(ALT, Try.orElse(this::throwReturn, this::alt)); + assertDoesNotThrow(() -> Try.orElse(this::noThrowNoReturn, this::fail)); + assertEquals(NORMAL, Try.orElse(this::noThrowReturn, this::alt)); + } + + @Test + void tryOrThrow() { + assertDoesNotThrow(() -> Try.orThrow(this::noThrowNoReturn)); + assertThrows(RuntimeException.class, () -> Try.orThrow(this::throwNoReturn)); + assertEquals(NORMAL, Try.orThrow(this::noThrowReturn)); + assertThrows(RuntimeException.class, () -> Try.orThrow(this::throwReturn)); + } + + @Test + void tryCompose() { + assertDoesNotThrow(() -> ((ThrowingSupplier) this::noThrowReturn).andThen(String::toCharArray).andThen(s -> {}).orThrow().run()); + assertThrows(RuntimeException.class, () -> ((ThrowingConsumer)(s -> {})).compose(String::toCharArray).compose(this::throwReturn).orThrow().run()); + } + + private void noop(T t) { + } + + private String alt(Throwable t) { + return ALT; + } + + private void fail(T t) { + throw new RuntimeException(); + } + + private String throwReturn() throws IOException { + throw new IOException(); + } + + private void throwNoReturn() throws IOException { + throw new IOException(); + } + + private String noThrowReturn() throws IOException { + return NORMAL; + } + + private void noThrowNoReturn() throws IOException { + } +}