V getAttachmentFromWrapperChain(final W wrapper, final K
}).orElse(null);
}
+ /**
+ * Gets the wrapper chain, aka. the list of all instances on the wrapper chain.
+ *
+ * The wrapper chain consists of wrapper itself, followed by the wrappers
+ * obtained by repeatedly calling {@link Wrapper#unwrap()}.
+ *
+ * @param wrapper wrapper instance
+ * @param the type of instances that be wrapped
+ * @throws NullPointerException if wrapped argument is null,
+ * or any wrapper {@link Wrapper#unwrap()} returns null,
+ * or the adaptee of {@link WrapperAdapter} is null
+ * @throws IllegalStateException if the adaptee of {@link WrapperAdapter} is an instance of {@link Wrapper}
+ * or CYCLIC wrapper chain
+ */
+ @NonNull
+ @Contract(pure = true)
+ public static List getInstancesOfWrapperChain(final W wrapper) {
+ List ret = new ArrayList<>();
+ forEachOnWrapperChain(wrapper, ret::add);
+ return ret;
+ }
+
+ /**
+ * Gets the base of the wrapper chain, aka. the last instance of the wrapper chain.
+ *
+ * The wrapper chain consists of wrapper itself, followed by the wrappers
+ * obtained by repeatedly calling {@link Wrapper#unwrap()}.
+ *
+ * @param wrapper wrapper instance
+ * @param the type of instances that be wrapped
+ * @throws NullPointerException if wrapped argument is null,
+ * or any wrapper {@link Wrapper#unwrap()} returns null,
+ * or the adaptee of {@link WrapperAdapter} is null
+ * @throws IllegalStateException if the adaptee of {@link WrapperAdapter} is an instance of {@link Wrapper}
+ * or CYCLIC wrapper chain
+ */
+ @NonNull
+ @Contract(pure = true)
+ @SuppressWarnings("unchecked")
+ public static W getBaseOfWrapperChain(final W wrapper) {
+ Object[] holder = new Object[1];
+ forEachOnWrapperChain(wrapper, w -> holder[0] = w);
+ return (W) holder[0];
+ }
+
+ /**
+ * Unwraps {@link Wrapper} to the underlying instance, a convenient method for {@link Wrapper#unwrap()}.
+ *
+ * This method is {@code null}-safe, return {@code null} iff input parameter is {@code null};
+ * If input parameter is not a {@link Wrapper} just return input.
+ *
+ * @param obj wrapper instance
+ * @param the type of instances that be wrapped
+ * @throws NullPointerException if {@link Wrapper#unwrap()} returns null
+ * @see Wrapper#unwrap()
+ * @see #isWrapper(Object)
+ */
+ @Nullable
+ @Contract(value = "null -> null; !null -> !null", pure = true)
+ @SuppressWarnings("unchecked")
+ public static W unwrap(@Nullable final W obj) {
+ if (!isWrapper(obj)) return obj;
+ return (W) unwrapNonNull(obj);
+ }
+
+ /**
+ * Checks the input object is an instance of {@link Wrapper} or not,
+ * return {@code false} if input {@code null}.
+ *
+ * @see #unwrap(Object)
+ */
+ @Contract(value = "null -> false", pure = true)
+ public static boolean isWrapper(@Nullable Object obj) {
+ return obj instanceof Wrapper;
+ }
+
/**
* Verifies the compliance of wrapper chain with the specification contracts.
*
+ * The wrapper chain consists of wrapper itself, followed by the wrappers
+ * obtained by repeatedly calling {@link Wrapper#unwrap()}.
+ *
* more about the specification contracts see the doc of below methods:
*
* - {@link Wrapper#unwrap()}
@@ -138,15 +219,46 @@ public static V getAttachmentFromWrapperChain(final W wrapper, final K
* or the adaptee of {@link WrapperAdapter} is null
* @throws IllegalStateException if the adaptee of {@link WrapperAdapter} is an instance of {@link Wrapper}
* or CYCLIC wrapper chain
- * @see Wrapper#unwrap()
- * @see WrapperAdapter#adaptee()
*/
public static void verifyWrapperChainContracts(final W wrapper) {
travelWrapperChain(wrapper, w -> Optional.empty());
}
+ /**
+ * Verifies the compliance of wrapper chain with the specification contracts,
+ * and checks all instances on wrapper chain is an instance of the given {@code bizInterface}.
+ *
+ * The wrapper chain consists of wrapper itself, followed by the wrappers
+ * obtained by repeatedly calling {@link Wrapper#unwrap()}.
+ *
+ * more about the specification contracts see the doc of below methods:
+ *
+ * - {@link Wrapper#unwrap()}
+ *
- {@link WrapperAdapter#adaptee()}
+ *
+ *
+ * @param wrapper wrapper instance
+ * @param the type of instances that be wrapped
+ * @throws NullPointerException if wrapped argument is null,
+ * or any wrapper {@link Wrapper#unwrap()} returns null,
+ * or the adaptee of {@link WrapperAdapter} is null
+ * @throws IllegalStateException if any instance on the wrapper chain is not an instance of {@code bizInterface},
+ * or the adaptee of {@link WrapperAdapter} is an instance of {@link Wrapper}
+ * or CYCLIC wrapper chain
+ */
+ public static void verifyWrapperChainContracts(final W wrapper, Class bizInterface) {
+ travelWrapperChain(wrapper, w -> {
+ if (!bizInterface.isAssignableFrom(w.getClass())) {
+ throw new IllegalStateException("The instance(" + w.getClass().getName() +
+ ") on wrapper chain is not an instance of " + bizInterface.getName());
+ }
+ return Optional.empty();
+ });
+ }
+
/**
* Reports whether any instance on the wrapper chain satisfies the given {@code predicate}.
+ * Exceptions thrown by the {@code predicate} are relayed to the caller.
*
* The wrapper chain consists of wrapper itself, followed by the wrappers
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
@@ -161,8 +273,6 @@ public static void verifyWrapperChainContracts(final W wrapper) {
* or the adaptee of {@link WrapperAdapter} is null
* @throws IllegalStateException if the adaptee of {@link WrapperAdapter} is an instance of {@link Wrapper}
* or CYCLIC wrapper chain
- * @see Wrapper#unwrap()
- * @see WrapperAdapter#adaptee()
*/
public static boolean testWrapperChain(final W wrapper, final Predicate super W> predicate) {
requireNonNull(wrapper, "wrapper is null");
@@ -173,10 +283,38 @@ public static boolean testWrapperChain(final W wrapper, final Predicate su
}).orElse(false);
}
+ /**
+ * Performs the given action for each instance on the wrapper chain
+ * until all elements have been processed or the action throws an exception.
+ * Exceptions thrown by the {@code action} are relayed to the caller.
+ *
+ * The wrapper chain consists of wrapper itself, followed by the wrappers
+ * obtained by repeatedly calling {@link Wrapper#unwrap()}.
+ *
+ * @param wrapper wrapper instance/wrapper chain
+ * @param action The action to be performed for each instance
+ * @param the type of instances that be wrapped
+ * @throws NullPointerException if any arguments is null,
+ * or any wrapper {@link Wrapper#unwrap()} returns null,
+ * or the adaptee of {@link WrapperAdapter} is null
+ * @throws IllegalStateException if the adaptee of {@link WrapperAdapter} is an instance of {@link Wrapper}
+ * or CYCLIC wrapper chain
+ * @see #travelWrapperChain(Object, Function)
+ */
+ public static void forEachOnWrapperChain(final W wrapper, final Consumer super W> action) {
+ requireNonNull(wrapper, "wrapper is null");
+ requireNonNull(action, "action is null");
+ travelWrapperChain(wrapper, w -> {
+ action.accept(w);
+ return Optional.empty();
+ });
+ }
+
/**
* Traverses the wrapper chain and applies the given {@code process} function to
* each instance on the wrapper chain, returns the first non-empty({@link Optional#empty()}) result
* of the process function, otherwise returns {@link Optional#empty()}.
+ * Exceptions thrown by the process function are relayed to the caller.
*
* The wrapper chain consists of wrapper itself, followed by the wrappers
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
@@ -192,12 +330,12 @@ public static boolean testWrapperChain(final W wrapper, final Predicate su
* or the adaptee of {@link WrapperAdapter} is null
* @throws IllegalStateException if the adaptee of {@link WrapperAdapter} is an instance of {@link Wrapper}
* or CYCLIC wrapper chain
- * @see Wrapper#unwrap()
- * @see WrapperAdapter#adaptee()
+ * @see #forEachOnWrapperChain(Object, Consumer)
*/
@NonNull
@SuppressWarnings("unchecked")
- public static Optional travelWrapperChain(final W wrapper, final Function super W, Optional> process) {
+ public static Optional travelWrapperChain(
+ final W wrapper, final Function super W, Optional> process) {
requireNonNull(wrapper, "wrapper is null");
requireNonNull(process, "process is null");
@@ -216,7 +354,7 @@ public static Optional travelWrapperChain(final W wrapper, final Funct
if (r.isPresent()) return r;
}
- if (!(w instanceof Wrapper)) return Optional.empty();
+ if (!isWrapper(w)) return Optional.empty();
w = unwrapNonNull(w);
if (history.containsKey(w)) {
throw new IllegalStateException("CYCLIC wrapper chain" +
@@ -228,13 +366,14 @@ public static Optional travelWrapperChain(final W wrapper, final Funct
/**
* Gets adaptee of the given WrapperAdapter instance with {@code null} check and non-{@link Wrapper} type check.
*/
+ @Contract(pure = true)
private static Object adapteeNonWrapper(final Object wrapper) {
final Object adaptee = ((WrapperAdapter>) wrapper).adaptee();
Supplier msg = () -> "adaptee of WrapperAdapter(" + wrapper.getClass().getName() + ") is null";
requireNonNull(adaptee, msg);
- if (adaptee instanceof Wrapper) {
+ if (isWrapper(adaptee)) {
throw new IllegalStateException("adaptee(" + adaptee.getClass().getName() +
") of WrapperAdapter(" + wrapper.getClass().getName() +
") is an instance of Wrapper, adapting a Wrapper to a Wrapper is UNNECESSARY");
@@ -246,6 +385,7 @@ private static Object adapteeNonWrapper(final Object wrapper) {
/**
* Unwraps the given wrapper instance with {@code null} check.
*/
+ @Contract(pure = true)
private static Object unwrapNonNull(final Object wrapper) {
Object unwrap = ((Wrapper>) wrapper).unwrap();
Supplier msg = () -> "unwrap of Wrapper(" + wrapper.getClass().getName() + ") is null";
diff --git a/src/test/java/io/foldright/demo/Demo.java b/src/test/java/io/foldright/demo/Demo.java
index 6bc4901..03aaab2 100644
--- a/src/test/java/io/foldright/demo/Demo.java
+++ b/src/test/java/io/foldright/demo/Demo.java
@@ -1,10 +1,8 @@
package io.foldright.demo;
-import io.foldright.inspectablewrappers.Inspector;
-
import java.util.concurrent.Executor;
-import static io.foldright.inspectablewrappers.Inspector.containsInstanceOnWrapperChain;
+import static io.foldright.inspectablewrappers.Inspector.containsInstanceTypeOnWrapperChain;
import static io.foldright.inspectablewrappers.Inspector.getAttachmentFromWrapperChain;
@@ -17,7 +15,7 @@ public static void main(String[] args) {
////////////////////////////////////////
System.out.println("Is executor lazy? " +
- containsInstanceOnWrapperChain(executor, LazyExecutorWrapper.class));
+ containsInstanceTypeOnWrapperChain(executor, LazyExecutorWrapper.class));
// print true
String busy = getAttachmentFromWrapperChain(executor, "busy");
diff --git a/src/test/java/io/foldright/demo/integration/IntegrationDemo.java b/src/test/java/io/foldright/demo/integration/IntegrationDemo.java
index 21fa0d6..06f7505 100644
--- a/src/test/java/io/foldright/demo/integration/IntegrationDemo.java
+++ b/src/test/java/io/foldright/demo/integration/IntegrationDemo.java
@@ -8,7 +8,7 @@
import java.util.concurrent.Executor;
-import static io.foldright.inspectablewrappers.Inspector.containsInstanceOnWrapperChain;
+import static io.foldright.inspectablewrappers.Inspector.containsInstanceTypeOnWrapperChain;
import static io.foldright.inspectablewrappers.Inspector.getAttachmentFromWrapperChain;
@@ -21,7 +21,7 @@ public static void main(String[] args) {
////////////////////////////////////////
System.out.println("Is executor ExistedExecutorWrapper? " +
- containsInstanceOnWrapperChain(executor, ExistedExecutorWrapper.class));
+ containsInstanceTypeOnWrapperChain(executor, ExistedExecutorWrapper.class));
// print true
String adaptAttachment = getAttachmentFromWrapperChain(executor, "adapted-existed-executor-wrapper-msg");
System.out.println("Adapted existed executor wrapper msg: " + adaptAttachment);
diff --git a/src/test/java/io/foldright/demo/integration/IntegrationDemoUsingWrapperAdapterUtils.java b/src/test/java/io/foldright/demo/integration/IntegrationDemoUsingWrapperAdapterUtils.java
index 9d0c227..7c5e9b0 100644
--- a/src/test/java/io/foldright/demo/integration/IntegrationDemoUsingWrapperAdapterUtils.java
+++ b/src/test/java/io/foldright/demo/integration/IntegrationDemoUsingWrapperAdapterUtils.java
@@ -7,7 +7,7 @@
import java.util.concurrent.Executor;
-import static io.foldright.inspectablewrappers.Inspector.containsInstanceOnWrapperChain;
+import static io.foldright.inspectablewrappers.Inspector.containsInstanceTypeOnWrapperChain;
import static io.foldright.inspectablewrappers.Inspector.getAttachmentFromWrapperChain;
@@ -20,7 +20,7 @@ public static void main(String[] args) {
////////////////////////////////////////
System.out.println("Is executor ExistedExecutorWrapper? " +
- containsInstanceOnWrapperChain(executor, ExistedExecutorWrapper.class));
+ containsInstanceTypeOnWrapperChain(executor, ExistedExecutorWrapper.class));
// print true
String adaptAttachment = getAttachmentFromWrapperChain(executor, "adapted-existed-executor-wrapper-msg");
System.out.println("Adapted existed executor wrapper msg: " + adaptAttachment);
diff --git a/src/test/java/io/foldright/inspectablewrappers/WrapperAdapterTest.kt b/src/test/java/io/foldright/inspectablewrappers/WrapperAdapterTest.kt
index 9d2f6e9..5074a5c 100644
--- a/src/test/java/io/foldright/inspectablewrappers/WrapperAdapterTest.kt
+++ b/src/test/java/io/foldright/inspectablewrappers/WrapperAdapterTest.kt
@@ -1,6 +1,6 @@
package io.foldright.inspectablewrappers
-import io.foldright.inspectablewrappers.Inspector.containsInstanceOnWrapperChain
+import io.foldright.inspectablewrappers.Inspector.containsInstanceTypeOnWrapperChain
import io.foldright.inspectablewrappers.Inspector.getAttachmentFromWrapperChain
import io.foldright.inspectablewrappers.utils.AttachableDelegate
import io.kotest.assertions.fail
@@ -25,10 +25,10 @@ class WrapperAdapterTest : FunSpec({
.let(::ChattyExecutorWrapper)
test("WrapperAdapter") {
- containsInstanceOnWrapperChain(executorChain, ExistedExecutorWrapper::class.java).shouldBeTrue()
- containsInstanceOnWrapperChain(executorChain, ExistedExecutorWrapperAdapter::class.java).shouldBeTrue()
- containsInstanceOnWrapperChain(executorChain, ChattyExecutorWrapper::class.java).shouldBeTrue()
- containsInstanceOnWrapperChain(executorChain, ExecutorService::class.java).shouldBeFalse()
+ containsInstanceTypeOnWrapperChain(executorChain, ExistedExecutorWrapper::class.java).shouldBeTrue()
+ containsInstanceTypeOnWrapperChain(executorChain, ExistedExecutorWrapperAdapter::class.java).shouldBeTrue()
+ containsInstanceTypeOnWrapperChain(executorChain, ChattyExecutorWrapper::class.java).shouldBeTrue()
+ containsInstanceTypeOnWrapperChain(executorChain, ExecutorService::class.java).shouldBeFalse()
val value: String? = getAttachmentFromWrapperChain(executorChain, ADAPTED_MSG_KEY)
value shouldBe ADAPTED_MSG_VALUE
@@ -62,11 +62,11 @@ class WrapperAdapterTest : FunSpec({
" is an instance of Wrapper, adapting a Wrapper to a Wrapper is UNNECESSARY"
shouldThrow {
- containsInstanceOnWrapperChain(chain, ExecutorService::class.java)
+ containsInstanceTypeOnWrapperChain(chain, ExecutorService::class.java)
}.message shouldBe errMsg
// first instance is ok, not trigger the check logic yet...
- containsInstanceOnWrapperChain(chain, Executor::class.java).shouldBeTrue()
- containsInstanceOnWrapperChain(chain, ChattyExecutorWrapperAdapter::class.java).shouldBeTrue()
+ containsInstanceTypeOnWrapperChain(chain, Executor::class.java).shouldBeTrue()
+ containsInstanceTypeOnWrapperChain(chain, ChattyExecutorWrapperAdapter::class.java).shouldBeTrue()
shouldThrow {
getAttachmentFromWrapperChain(chain, "k1")
diff --git a/src/test/java/io/foldright/inspectablewrappers/WrapperTest.kt b/src/test/java/io/foldright/inspectablewrappers/WrapperTest.kt
index 1adff05..54a4dae 100644
--- a/src/test/java/io/foldright/inspectablewrappers/WrapperTest.kt
+++ b/src/test/java/io/foldright/inspectablewrappers/WrapperTest.kt
@@ -1,6 +1,6 @@
package io.foldright.inspectablewrappers
-import io.foldright.inspectablewrappers.Inspector.containsInstanceOnWrapperChain
+import io.foldright.inspectablewrappers.Inspector.containsInstanceTypeOnWrapperChain
import io.foldright.inspectablewrappers.Inspector.getAttachmentFromWrapperChain
import io.foldright.inspectablewrappers.utils.AttachableDelegate
import io.kotest.assertions.fail
@@ -21,9 +21,9 @@ class WrapperTest : FunSpec({
.let(::ChattyExecutorWrapper)
test("wrapper") {
- containsInstanceOnWrapperChain(executorChain, LazyExecutorWrapper::class.java).shouldBeTrue()
- containsInstanceOnWrapperChain(executorChain, ChattyExecutorWrapper::class.java).shouldBeTrue()
- containsInstanceOnWrapperChain(executorChain, ExecutorService::class.java).shouldBeFalse()
+ containsInstanceTypeOnWrapperChain(executorChain, LazyExecutorWrapper::class.java).shouldBeTrue()
+ containsInstanceTypeOnWrapperChain(executorChain, ChattyExecutorWrapper::class.java).shouldBeTrue()
+ containsInstanceTypeOnWrapperChain(executorChain, ExecutorService::class.java).shouldBeFalse()
val value: String? = getAttachmentFromWrapperChain(executorChain, "busy")
value shouldBe "very, very busy!"
@@ -49,12 +49,12 @@ class WrapperTest : FunSpec({
}.message shouldBe "key is null"
}
- test("inspect last instance - containsInstanceOnWrapperChain") {
+ test("inspect last instance - containsInstanceTypeOnWrapperChain") {
val pool = Executors.newCachedThreadPool()
- containsInstanceOnWrapperChain(pool, ExecutorService::class.java).shouldBeTrue()
+ containsInstanceTypeOnWrapperChain(pool, ExecutorService::class.java).shouldBeTrue()
val chatty = ChattyExecutorWrapper(pool)
- containsInstanceOnWrapperChain(chatty, ExecutorService::class.java).shouldBeTrue()
+ containsInstanceTypeOnWrapperChain(chatty, ExecutorService::class.java).shouldBeTrue()
}
test("inspect last instance - getAttachmentFromWrapperChain") {
diff --git a/src/test/java/io/foldright/inspectablewrappers/utils/WrapperAdapterUtilsTest.kt b/src/test/java/io/foldright/inspectablewrappers/utils/WrapperAdapterUtilsTest.kt
index d3a1141..2689b3c 100644
--- a/src/test/java/io/foldright/inspectablewrappers/utils/WrapperAdapterUtilsTest.kt
+++ b/src/test/java/io/foldright/inspectablewrappers/utils/WrapperAdapterUtilsTest.kt
@@ -36,9 +36,9 @@ class WrapperAdapterUtilsTest : FunSpec({
}
.let(::ChattyExecutorWrapper)
- Inspector.containsInstanceOnWrapperChain(chain, ExistedExecutorWrapper::class.java).shouldBeTrue()
- Inspector.containsInstanceOnWrapperChain(chain, ChattyExecutorWrapper::class.java).shouldBeTrue()
- Inspector.containsInstanceOnWrapperChain(chain, ExecutorService::class.java).shouldBeFalse()
+ Inspector.containsInstanceTypeOnWrapperChain(chain, ExistedExecutorWrapper::class.java).shouldBeTrue()
+ Inspector.containsInstanceTypeOnWrapperChain(chain, ChattyExecutorWrapper::class.java).shouldBeTrue()
+ Inspector.containsInstanceTypeOnWrapperChain(chain, ExecutorService::class.java).shouldBeFalse()
val value: String? = Inspector.getAttachmentFromWrapperChain(chain, ADAPTED_MSG_KEY)
value shouldBe ADAPTED_MSG_VALUE
@@ -60,9 +60,9 @@ class WrapperAdapterUtilsTest : FunSpec({
}
.let(::ChattyExecutorWrapper)
- Inspector.containsInstanceOnWrapperChain(chain, ExistedExecutorWrapper::class.java).shouldBeTrue()
- Inspector.containsInstanceOnWrapperChain(chain, ChattyExecutorWrapper::class.java).shouldBeTrue()
- Inspector.containsInstanceOnWrapperChain(chain, ExecutorService::class.java).shouldBeFalse()
+ Inspector.containsInstanceTypeOnWrapperChain(chain, ExistedExecutorWrapper::class.java).shouldBeTrue()
+ Inspector.containsInstanceTypeOnWrapperChain(chain, ChattyExecutorWrapper::class.java).shouldBeTrue()
+ Inspector.containsInstanceTypeOnWrapperChain(chain, ExecutorService::class.java).shouldBeFalse()
Inspector.getAttachmentFromWrapperChain(chain, "not existed").shouldBeNull()