diff --git a/README.md b/README.md index e2a3332..75bb778 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,8 @@ The purpose of **Inspectable Wrappers** is to provide a standard for wrapper cha enhance the wrapper instances with the attachment storage ability - [`WrapperAdapter`](src/main/java/io/foldright/inspectablewrappers/WrapperAdapter.java) interface is used to adapt an existed wrapper instance to type `Wrapper` without modifying it -- The [`Inspector`](src/main/java/io/foldright/inspectablewrappers/Inspector.java) class is used to inspect the **wrapper +- The [`Inspector`](src/main/java/io/foldright/inspectablewrappers/Inspector.java) class is used to inspect the * + *wrapper chain** ## 🌰 Usage Demo @@ -125,10 +126,10 @@ public class Demo { //////////////////////////////////////// System.out.println("Is executor lazy? " + - Inspector.isInstanceOf(executor, LazyExecutorWrapper.class)); + containsInstanceOnWrapperChain(executor, LazyExecutorWrapper.class)); // print true - String busy = Inspector.getAttachment(executor, "busy"); + String busy = getAttachmentFromWrapperChain(executor, "busy"); System.out.println("Is executor busy? " + busy); // print "very, very busy!" @@ -205,9 +206,9 @@ public class IntegrationDemo { //////////////////////////////////////// System.out.println("Is executor ExistedExecutorWrapper? " + - Inspector.isInstanceOf(executor, ExistedExecutorWrapper.class)); + containsInstanceOnWrapperChain(executor, ExistedExecutorWrapper.class)); // print true - String adaptAttachment = Inspector.getAttachment(executor, "adapted-existed-executor-wrapper-msg"); + String adaptAttachment = getAttachmentFromWrapperChain(executor, "adapted-existed-executor-wrapper-msg"); System.out.println("Adapted existed executor wrapper msg: " + adaptAttachment); // print "I'm an adapter of an existed executor which have nothing to do with ~inspectable~wrappers~." diff --git a/src/main/java/io/foldright/inspectablewrappers/Attachable.java b/src/main/java/io/foldright/inspectablewrappers/Attachable.java index 28ac45b..5445713 100644 --- a/src/main/java/io/foldright/inspectablewrappers/Attachable.java +++ b/src/main/java/io/foldright/inspectablewrappers/Attachable.java @@ -8,7 +8,8 @@ * This {@code Attachable} interface is used to be implemented by wrapper classes, * provide the attachment storage ability. *

- * Retrieves the attachment from wrapper chain the static method {@link Inspector#getAttachment(Object, Object)}. + * Retrieves the attachment from wrapper chain + * by method {@link Inspector#getAttachmentFromWrapperChain(Object, Object)}. *

* Provided {@link io.foldright.inspectablewrappers.utils.AttachableDelegate AttachableDelegate} * as a simple delegate implementation. @@ -17,7 +18,7 @@ * @param the value type to be stored * @author Jerry Lee (oldratlee at gmail dot com) * @author Yang Fang (snoop dot fy at gmail dot com) - * @see Inspector#getAttachment(Object, Object) + * @see Inspector#getAttachmentFromWrapperChain(Object, Object) * @see io.foldright.inspectablewrappers.utils.AttachableDelegate */ public interface Attachable { @@ -37,7 +38,7 @@ public interface Attachable { * @return return the attachment value, or {@code null} if contains no attachment for the key * @throws NullPointerException if key argument is null * @throws ClassCastException if the return value is not type {@code } - * @see Inspector#getAttachment(Object, Object) + * @see Inspector#getAttachmentFromWrapperChain(Object, Object) */ @Nullable V getAttachment(@NonNull K key); diff --git a/src/main/java/io/foldright/inspectablewrappers/Inspector.java b/src/main/java/io/foldright/inspectablewrappers/Inspector.java index 7c1f172..43c6829 100644 --- a/src/main/java/io/foldright/inspectablewrappers/Inspector.java +++ b/src/main/java/io/foldright/inspectablewrappers/Inspector.java @@ -18,21 +18,29 @@ *

Common simple usages

* * *

Advanced usages

* *

* You can implement your own inspection logic using above advanced methods. * + *

Note about usage and methods naming

+ *

+ * All method names contain the word "wrapper chain", + * so the usage code is easily recognizable as related to {@code inspectable wrappers}. + *

+ * Because the method names are long and informative, + * it's recommended to {@code static import} these methods. + * * @author Jerry Lee (oldratlee at gmail dot com) * @author Zava Xu (zava dot kid at gmail dot com) * @see Wrapper @@ -47,19 +55,26 @@ public final class Inspector { * 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 clazz target type - * @param the type of instances that be wrapped + * @param wrapper wrapper instance/wrapper chain + * @param instanceType target type + * @param the type of instances that be wrapped * @return return {@code false} if no wrapper on the wrapper chain matches the given type, * otherwise return {@code true} - * @throws NullPointerException if any arguments is null or any wrapper {@link Wrapper#unwrap()} returns null + * @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 type {@link Wrapper} + * @see Wrapper#unwrap() * @see WrapperAdapter#adaptee() */ - public static boolean isInstanceOf(final W wrapper, final Class clazz) { + public static boolean containsInstanceOnWrapperChain(final W wrapper, final Class instanceType) { requireNonNull(wrapper, "wrapper is null"); - requireNonNull(clazz, "clazz is null"); - return inspect(wrapper, w -> clazz.isAssignableFrom(w.getClass())); + requireNonNull(instanceType, "instanceType is null"); + return testWrapperChain(wrapper, w -> isInstanceOf(w, instanceType)); + } + + private static boolean isInstanceOf(final Object o, final Class clazz) { + return clazz.isAssignableFrom(o.getClass()); } /** @@ -78,17 +93,19 @@ public static boolean isInstanceOf(final W wrapper, final Class clazz) { * @param the type of attachment value * @return the attachment value of wrapper for given key on the wrapper chain, * or null if the attachment is absent - * @throws NullPointerException if any arguments is null or any wrapper {@link Wrapper#unwrap()} returns null + * @throws NullPointerException if any arguments is null, + * or any wrapper {@link Wrapper#unwrap()} returns null, + * or the adaptee of {@link WrapperAdapter} is null * @throws ClassCastException if the return value is not type {@code } * @throws IllegalStateException if the adaptee of {@link WrapperAdapter} is type {@link Wrapper} * @see Attachable#getAttachment(Object) */ @Nullable @SuppressWarnings("unchecked") - public static V getAttachment(final W wrapper, final K key) { + public static V getAttachmentFromWrapperChain(final W wrapper, final K key) { requireNonNull(wrapper, "wrapper is null"); requireNonNull(key, "key is null"); - return travel(wrapper, w -> { + return travelWrapperChain(wrapper, w -> { if (w instanceof Attachable) { V value = ((Attachable) w).getAttachment(key); return Optional.ofNullable(value); @@ -109,13 +126,15 @@ public static V getAttachment(final W wrapper, final K key) { * @param the type of instances that be wrapped * @return return {@code false} if no wrapper on the wrapper chain satisfy the given {@code predicate}, * otherwise return {@code true} - * @throws NullPointerException if any arguments is null or any wrapper {@link Wrapper#unwrap()} returns null + * @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 type {@link Wrapper} */ - public static boolean inspect(final W wrapper, final Predicate predicate) { + public static boolean testWrapperChain(final W wrapper, final Predicate predicate) { requireNonNull(wrapper, "wrapper is null"); requireNonNull(predicate, "predicate is null"); - return travel(wrapper, w -> { + return travelWrapperChain(wrapper, w -> { if (predicate.test(w)) return Optional.of(true); else return Optional.empty(); }).orElse(false); @@ -135,12 +154,14 @@ public static boolean inspect(final W wrapper, final Predicate pr * @param the return data type of process function * @return the first non-empty({@link Optional#empty()}) result of the process function, * otherwise returns {@link Optional#empty()} - * @throws NullPointerException if any arguments is null or any wrapper {@link Wrapper#unwrap()} returns null + * @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 type {@link Wrapper} */ @NonNull @SuppressWarnings("unchecked") - public static Optional travel(final W wrapper, final Function> process) { + public static Optional travelWrapperChain(final W wrapper, final Function> process) { requireNonNull(wrapper, "wrapper is null"); requireNonNull(process, "process is null"); diff --git a/src/test/java/io/foldright/demo/Demo.java b/src/test/java/io/foldright/demo/Demo.java index 578284d..6bc4901 100644 --- a/src/test/java/io/foldright/demo/Demo.java +++ b/src/test/java/io/foldright/demo/Demo.java @@ -4,6 +4,9 @@ import java.util.concurrent.Executor; +import static io.foldright.inspectablewrappers.Inspector.containsInstanceOnWrapperChain; +import static io.foldright.inspectablewrappers.Inspector.getAttachmentFromWrapperChain; + public class Demo { public static void main(String[] args) { @@ -14,10 +17,10 @@ public static void main(String[] args) { //////////////////////////////////////// System.out.println("Is executor lazy? " + - Inspector.isInstanceOf(executor, LazyExecutorWrapper.class)); + containsInstanceOnWrapperChain(executor, LazyExecutorWrapper.class)); // print true - String busy = Inspector.getAttachment(executor, "busy"); + String busy = getAttachmentFromWrapperChain(executor, "busy"); System.out.println("Is executor busy? " + busy); // print "very, very busy!" diff --git a/src/test/java/io/foldright/demo/integration/ExistedExecutorWrapper.java b/src/test/java/io/foldright/demo/integration/ExistedExecutorWrapper.java index 458eed8..7524247 100644 --- a/src/test/java/io/foldright/demo/integration/ExistedExecutorWrapper.java +++ b/src/test/java/io/foldright/demo/integration/ExistedExecutorWrapper.java @@ -21,7 +21,7 @@ public Executor getExecutor() { @Override public void execute(Runnable command) { - System.out.println("I'm a adapter of an existed executor which have nothing to do with ~inspectable~wrappers~."); + System.out.println("I'm existed executor, have nothing to do with ~inspectable~wrappers~."); executor.execute(command); } } diff --git a/src/test/java/io/foldright/demo/integration/IntegrationDemo.java b/src/test/java/io/foldright/demo/integration/IntegrationDemo.java index 53d0983..b8ca703 100644 --- a/src/test/java/io/foldright/demo/integration/IntegrationDemo.java +++ b/src/test/java/io/foldright/demo/integration/IntegrationDemo.java @@ -4,13 +4,15 @@ import edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault; import io.foldright.demo.ChattyExecutorWrapper; import io.foldright.inspectablewrappers.Attachable; -import io.foldright.inspectablewrappers.Inspector; import io.foldright.inspectablewrappers.WrapperAdapter; import io.foldright.inspectablewrappers.utils.AttachableDelegate; import javax.annotation.ParametersAreNonnullByDefault; import java.util.concurrent.Executor; +import static io.foldright.inspectablewrappers.Inspector.containsInstanceOnWrapperChain; +import static io.foldright.inspectablewrappers.Inspector.getAttachmentFromWrapperChain; + @ParametersAreNonnullByDefault @ReturnValuesAreNonnullByDefault @@ -23,9 +25,9 @@ public static void main(String[] args) { //////////////////////////////////////// System.out.println("Is executor ExistedExecutorWrapper? " + - Inspector.isInstanceOf(executor, ExistedExecutorWrapper.class)); + containsInstanceOnWrapperChain(executor, ExistedExecutorWrapper.class)); // print true - String adaptAttachment = Inspector.getAttachment(executor, "adapted-existed-executor-wrapper-msg"); + String adaptAttachment = getAttachmentFromWrapperChain(executor, "adapted-existed-executor-wrapper-msg"); System.out.println("Adapted existed executor wrapper msg: " + adaptAttachment); // print "I'm an adapter of an existed executor which have nothing to do with ~inspectable~wrappers~." diff --git a/src/test/java/io/foldright/inspectablewrappers/WrapperAdapterTest.kt b/src/test/java/io/foldright/inspectablewrappers/WrapperAdapterTest.kt index f519bea..154e48e 100644 --- a/src/test/java/io/foldright/inspectablewrappers/WrapperAdapterTest.kt +++ b/src/test/java/io/foldright/inspectablewrappers/WrapperAdapterTest.kt @@ -1,5 +1,7 @@ package io.foldright.inspectablewrappers +import io.foldright.inspectablewrappers.Inspector.containsInstanceOnWrapperChain +import io.foldright.inspectablewrappers.Inspector.getAttachmentFromWrapperChain import io.foldright.inspectablewrappers.utils.AttachableDelegate import io.kotest.assertions.fail import io.kotest.assertions.throwables.shouldThrow @@ -25,20 +27,20 @@ class WrapperAdapterTest : FunSpec({ }.let(::ChattyExecutorWrapper) test("WrapperAdapter") { - Inspector.isInstanceOf(executorChain, ExistedExecutorWrapper::class.java).shouldBeTrue() - Inspector.isInstanceOf(executorChain, ExistedExecutorWrapperAdapter::class.java).shouldBeTrue() - Inspector.isInstanceOf(executorChain, ChattyExecutorWrapper::class.java).shouldBeTrue() - Inspector.isInstanceOf(executorChain, ExecutorService::class.java).shouldBeFalse() + containsInstanceOnWrapperChain(executorChain, ExistedExecutorWrapper::class.java).shouldBeTrue() + containsInstanceOnWrapperChain(executorChain, ExistedExecutorWrapperAdapter::class.java).shouldBeTrue() + containsInstanceOnWrapperChain(executorChain, ChattyExecutorWrapper::class.java).shouldBeTrue() + containsInstanceOnWrapperChain(executorChain, ExecutorService::class.java).shouldBeFalse() - val value: String? = Inspector.getAttachment(executorChain, ADAPTED_MSG_KEY) + val value: String? = getAttachmentFromWrapperChain(executorChain, ADAPTED_MSG_KEY) value shouldBe ADAPTED_MSG_VALUE - Inspector.getAttachment(executorChain, "not existed").shouldBeNull() + getAttachmentFromWrapperChain(executorChain, "not existed").shouldBeNull() } test("ClassCastException") { shouldThrow { - val value = Inspector.getAttachment(executorChain, ADAPTED_MSG_KEY) + val value = getAttachmentFromWrapperChain(executorChain, ADAPTED_MSG_KEY) fail(value.toString()) } } @@ -46,15 +48,15 @@ class WrapperAdapterTest : FunSpec({ @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") test("argument null") { shouldThrow { - Inspector.getAttachment(null, ADAPTED_MSG_KEY) + getAttachmentFromWrapperChain(null, ADAPTED_MSG_KEY) }.message shouldBe "wrapper is null" shouldThrow { - Inspector.getAttachment(executorChain, null) + getAttachmentFromWrapperChain(executorChain, null) }.message shouldBe "key is null" } - test("travel IllegalStateException - the adaptee of WrapperAdapter is a wrapper instance") { + test("travelWrapperChain IllegalStateException - the adaptee of WrapperAdapter is type wrapper") { val chain: Executor = ChattyExecutorWrapper { runnable -> runnable.run() } .let(::ChattyExecutorWrapperAdapter) @@ -63,15 +65,15 @@ class WrapperAdapterTest : FunSpec({ " is type Wrapper, adapting a Wrapper to a Wrapper is unnecessary!" shouldThrow { - Inspector.isInstanceOf(chain, ExecutorService::class.java) + containsInstanceOnWrapperChain(chain, ExecutorService::class.java) }.message shouldBe errMsg // first instance is ok, not trigger the check logic yet... - Inspector.isInstanceOf(chain, Executor::class.java).shouldBeTrue() - Inspector.isInstanceOf(chain, ChattyExecutorWrapperAdapter::class.java).shouldBeTrue() + containsInstanceOnWrapperChain(chain, Executor::class.java).shouldBeTrue() + containsInstanceOnWrapperChain(chain, ChattyExecutorWrapperAdapter::class.java).shouldBeTrue() shouldThrow { - Inspector.getAttachment(chain, "k1") + getAttachmentFromWrapperChain(chain, "k1") }.message shouldBe errMsg } }) diff --git a/src/test/java/io/foldright/inspectablewrappers/WrapperTest.kt b/src/test/java/io/foldright/inspectablewrappers/WrapperTest.kt index e160a41..1adff05 100644 --- a/src/test/java/io/foldright/inspectablewrappers/WrapperTest.kt +++ b/src/test/java/io/foldright/inspectablewrappers/WrapperTest.kt @@ -1,5 +1,7 @@ package io.foldright.inspectablewrappers +import io.foldright.inspectablewrappers.Inspector.containsInstanceOnWrapperChain +import io.foldright.inspectablewrappers.Inspector.getAttachmentFromWrapperChain import io.foldright.inspectablewrappers.utils.AttachableDelegate import io.kotest.assertions.fail import io.kotest.assertions.throwables.shouldThrow @@ -19,19 +21,19 @@ class WrapperTest : FunSpec({ .let(::ChattyExecutorWrapper) test("wrapper") { - Inspector.isInstanceOf(executorChain, LazyExecutorWrapper::class.java).shouldBeTrue() - Inspector.isInstanceOf(executorChain, ChattyExecutorWrapper::class.java).shouldBeTrue() - Inspector.isInstanceOf(executorChain, ExecutorService::class.java).shouldBeFalse() + containsInstanceOnWrapperChain(executorChain, LazyExecutorWrapper::class.java).shouldBeTrue() + containsInstanceOnWrapperChain(executorChain, ChattyExecutorWrapper::class.java).shouldBeTrue() + containsInstanceOnWrapperChain(executorChain, ExecutorService::class.java).shouldBeFalse() - val value: String? = Inspector.getAttachment(executorChain, "busy") + val value: String? = getAttachmentFromWrapperChain(executorChain, "busy") value shouldBe "very, very busy!" - Inspector.getAttachment(executorChain, "not existed").shouldBeNull() + getAttachmentFromWrapperChain(executorChain, "not existed").shouldBeNull() } test("ClassCastException") { shouldThrow { - val value = Inspector.getAttachment(executorChain, "busy") + val value = getAttachmentFromWrapperChain(executorChain, "busy") fail(value.toString()) } } @@ -39,25 +41,25 @@ class WrapperTest : FunSpec({ @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") test("argument null") { shouldThrow { - Inspector.getAttachment(null, "busy") + getAttachmentFromWrapperChain(null, "busy") }.message shouldBe "wrapper is null" shouldThrow { - Inspector.getAttachment(executorChain, null) + getAttachmentFromWrapperChain(executorChain, null) }.message shouldBe "key is null" } - test("inspect last instance - isInstanceOf") { + test("inspect last instance - containsInstanceOnWrapperChain") { val pool = Executors.newCachedThreadPool() - Inspector.isInstanceOf(pool, ExecutorService::class.java).shouldBeTrue() + containsInstanceOnWrapperChain(pool, ExecutorService::class.java).shouldBeTrue() val chatty = ChattyExecutorWrapper(pool) - Inspector.isInstanceOf(chatty, ExecutorService::class.java).shouldBeTrue() + containsInstanceOnWrapperChain(chatty, ExecutorService::class.java).shouldBeTrue() } - test("inspect last instance - getAttachment") { + test("inspect last instance - getAttachmentFromWrapperChain") { val attachable = AttachableDelegate().apply { setAttachment("k1", "v1") } - Inspector.getAttachment(attachable, "k1") shouldBe "v1" + getAttachmentFromWrapperChain(attachable, "k1") shouldBe "v1" val base = object : Executor, Attachable by AttachableDelegate() { override fun execute(command: Runnable) { @@ -65,10 +67,10 @@ class WrapperTest : FunSpec({ } } base.setAttachment("k1", "v1") - Inspector.getAttachment(base, "k1") shouldBe "v1" + getAttachmentFromWrapperChain(base, "k1") shouldBe "v1" val c2 = ChattyExecutorWrapper(base) - Inspector.getAttachment(c2, "k1") shouldBe "v1" + getAttachmentFromWrapperChain(c2, "k1") shouldBe "v1" } })