diff --git a/src/main/java/io/foldright/inspectablewrappers/Inspector.java b/src/main/java/io/foldright/inspectablewrappers/Inspector.java index 43c6829..1aeb590 100644 --- a/src/main/java/io/foldright/inspectablewrappers/Inspector.java +++ b/src/main/java/io/foldright/inspectablewrappers/Inspector.java @@ -21,6 +21,8 @@ * by static method {@link #containsInstanceOnWrapperChain(Object, Class)} *
  • Retrieves the attachment of instance on the wrapper chain * by static method {@link #getAttachmentFromWrapperChain(Object, Object)} + *
  • Verifies the compliance of wrapper chain with the specification contracts + * by static method {@link #verifyWrapperChainContracts(Object)} * * *

    Advanced usages

    @@ -99,6 +101,8 @@ private static boolean isInstanceOf(final Object o, final Class clazz) { * @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) + * @see Wrapper#unwrap() + * @see WrapperAdapter#adaptee() */ @Nullable @SuppressWarnings("unchecked") @@ -115,6 +119,29 @@ public static V getAttachmentFromWrapperChain(final W wrapper, final K }).orElse(null); } + /** + * Verifies the compliance of wrapper chain with the specification contracts. + *

    + * 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 the adaptee of {@link WrapperAdapter} is type {@link Wrapper} + * @see Wrapper#unwrap() + * @see WrapperAdapter#adaptee() + */ + public static void verifyWrapperChainContracts(final W wrapper) { + requireNonNull(wrapper, "wrapper is null"); + travelWrapperChain(wrapper, w -> Optional.empty()); + } + /** * Reports whether any instance on the wrapper chain satisfies the given {@code predicate}. *

    @@ -130,6 +157,8 @@ public static V getAttachmentFromWrapperChain(final W wrapper, final K * 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 testWrapperChain(final W wrapper, final Predicate predicate) { requireNonNull(wrapper, "wrapper is null"); @@ -158,6 +187,8 @@ public static boolean testWrapperChain(final W wrapper, final Predicate { + }; + + @Test + void test_null_unwrap() { + Executor w = new WrapperImpl(null); + + NullPointerException e = Assertions.assertThrowsExactly(NullPointerException.class, () -> verifyWrapperChainContracts(w)); + String expected = "unwrap of Wrapper(io.foldright.inspectablewrappers.SpecificationContractsTest$WrapperImpl) is null"; + assertEquals(expected, e.getMessage()); + } + + @Test + void test_null_adaptee() { + Executor w = new WrapperAdapterImpl(DUMMY, null); + + NullPointerException e = Assertions.assertThrowsExactly(NullPointerException.class, () -> verifyWrapperChainContracts(w)); + String expected = "adaptee of WrapperAdapter(io.foldright.inspectablewrappers.SpecificationContractsTest$WrapperAdapterImpl) is null"; + assertEquals(expected, e.getMessage()); + } + + @Test + void test_Wrap_type_adaptee() { + Executor w = new WrapperAdapterImpl(DUMMY, new WrapperImpl(null)); + + IllegalStateException e = Assertions.assertThrowsExactly(IllegalStateException.class, () -> verifyWrapperChainContracts(w)); + String expected = "adaptee(io.foldright.inspectablewrappers.SpecificationContractsTest$WrapperImpl)" + + " of WrapperAdapter(io.foldright.inspectablewrappers.SpecificationContractsTest$WrapperAdapterImpl) is type Wrapper," + + " adapting a Wrapper to a Wrapper is unnecessary!"; + assertEquals(expected, e.getMessage()); + } + + private static class WrapperImpl implements Wrapper, Executor { + private final Executor instance; + + WrapperImpl(Executor instance) { + this.instance = instance; + } + + @Override + public Executor unwrap() { + return instance; + } + + @Override + public void execute(Runnable command) { + } + } + + private static class WrapperAdapterImpl implements WrapperAdapter, Executor { + private final Executor wrapper; + private final Executor adaptee; + + WrapperAdapterImpl(Executor wrapper, Executor adaptee) { + this.wrapper = wrapper; + this.adaptee = adaptee; + } + + @Override + public Executor unwrap() { + return wrapper; + } + + @Override + public Executor adaptee() { + return adaptee; + } + + @Override + public void execute(Runnable command) { + } + } +}