Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
oldratlee committed Apr 7, 2024
1 parent 0fc9cef commit ea6afd7
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 47 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public class Demo {
////////////////////////////////////////

System.out.println("Is executor lazy? " +
containsInstanceOnWrapperChain(executor, LazyExecutorWrapper.class));
containsInstanceTypeOnWrapperChain(executor, LazyExecutorWrapper.class));
// print true

String busy = getAttachmentFromWrapperChain(executor, "busy");
Expand Down Expand Up @@ -208,7 +208,7 @@ public class IntegrationDemo {
////////////////////////////////////////

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);
Expand Down Expand Up @@ -305,7 +305,7 @@ public class IntegrationDemoUsingWrapperAdapterUtils {
////////////////////////////////////////

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);
Expand Down
170 changes: 155 additions & 15 deletions src/main/java/io/foldright/inspectablewrappers/Inspector.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
import edu.umd.cs.findbugs.annotations.DefaultAnnotationForParameters;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import org.jetbrains.annotations.Contract;

import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
Expand All @@ -19,7 +23,7 @@
* <h2>Common simple usages</h2>
* <ul>
* <li>Reports whether any instance on the wrapper chain matches the given type
* by static method {@link #containsInstanceOnWrapperChain(Object, Class)}
* by static method {@link #containsInstanceTypeOnWrapperChain(Object, Class)}
* <li>Retrieves the attachment of instance on the wrapper chain
* by static method {@link #getAttachmentFromWrapperChain(Object, Object)}
* <li>Verifies the compliance of wrapper chain with the specification contracts
Expand Down Expand Up @@ -68,10 +72,9 @@ public final class Inspector {
* 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 <W> boolean containsInstanceOnWrapperChain(final W wrapper, final Class<?> instanceType) {
@Contract(pure = true)
public static <W> boolean containsInstanceTypeOnWrapperChain(final W wrapper, final Class<?> instanceType) {
requireNonNull(wrapper, "wrapper is null");
requireNonNull(instanceType, "instanceType is null");
return testWrapperChain(wrapper, w -> isInstanceOf(w, instanceType));
Expand Down Expand Up @@ -104,10 +107,9 @@ private static boolean isInstanceOf(final Object o, final Class<?> clazz) {
* @throws IllegalStateException if the adaptee of {@link WrapperAdapter} is an instance of {@link Wrapper}
* or CYCLIC wrapper chain
* @see Attachable#getAttachment(Object)
* @see Wrapper#unwrap()
* @see WrapperAdapter#adaptee()
*/
@Nullable
@Contract(pure = true)
@SuppressWarnings("unchecked")
public static <W, K, V> V getAttachmentFromWrapperChain(final W wrapper, final K key) {
requireNonNull(wrapper, "wrapper is null");
Expand All @@ -122,9 +124,57 @@ public static <W, K, V> V getAttachmentFromWrapperChain(final W wrapper, final K
}).orElse(null);
}

/**
* Gets the wrapper chain, aka. the list of all instances on the wrapper chain.
* <p>
* The wrapper chain consists of wrapper itself, followed by the wrappers
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
*
* @param wrapper wrapper instance
* @param <W> 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 <W> List<W> getInstancesOfWrapperChain(final W wrapper) {
List<W> ret = new ArrayList<>();
forEachOnWrapperChain(wrapper, ret::add);
return ret;
}

/**
* Gets the base of the wrapper chain, aka. the last instance of the wrapper chain.
* <p>
* The wrapper chain consists of wrapper itself, followed by the wrappers
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
*
* @param wrapper wrapper instance
* @param <W> 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> W getBaseOfWrapperChain(final W wrapper) {
Object[] holder = new Object[1];
forEachOnWrapperChain(wrapper, w -> holder[0] = w);
return (W) holder[0];
}

/**
* Verifies the compliance of wrapper chain with the specification contracts.
* <p>
* The wrapper chain consists of wrapper itself, followed by the wrappers
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
* <p>
* more about the specification contracts see the doc of below methods:
* <ul>
* <li>{@link Wrapper#unwrap()}
Expand All @@ -138,15 +188,77 @@ public static <W, K, V> 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 <W> 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}.
* <p>
* The wrapper chain consists of wrapper itself, followed by the wrappers
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
* <p>
* more about the specification contracts see the doc of below methods:
* <ul>
* <li>{@link Wrapper#unwrap()}
* <li>{@link WrapperAdapter#adaptee()}
* </ul>
*
* @param wrapper wrapper instance
* @param <W> 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 <W> void verifyWrapperChainContracts(final W wrapper, Class<W> 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();
});
}

/**
* Unwraps {@link Wrapper} to the underlying instance, a convenient method for {@link Wrapper#unwrap()}.
* <p>
* 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 <W> 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> 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;
}

/**
* Reports whether any instance on the wrapper chain satisfies the given {@code predicate}.
* Exceptions thrown by the {@code predicate} are relayed to the caller.
* <p>
* The wrapper chain consists of wrapper itself, followed by the wrappers
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
Expand All @@ -161,8 +273,6 @@ public static <W> 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 <W> boolean testWrapperChain(final W wrapper, final Predicate<? super W> predicate) {
requireNonNull(wrapper, "wrapper is null");
Expand All @@ -173,10 +283,38 @@ public static <W> 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.
* <p>
* 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 <W> 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 <W> 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.
* <p>
* The wrapper chain consists of wrapper itself, followed by the wrappers
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
Expand All @@ -192,12 +330,12 @@ public static <W> 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 <W, T> Optional<T> travelWrapperChain(final W wrapper, final Function<? super W, Optional<T>> process) {
public static <W, T> Optional<T> travelWrapperChain(
final W wrapper, final Function<? super W, Optional<T>> process) {
requireNonNull(wrapper, "wrapper is null");
requireNonNull(process, "process is null");

Expand All @@ -216,7 +354,7 @@ public static <W, T> Optional<T> 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" +
Expand All @@ -228,13 +366,14 @@ public static <W, T> Optional<T> 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<String> 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");
Expand All @@ -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<String> msg = () -> "unwrap of Wrapper(" + wrapper.getClass().getName() + ") is null";
Expand Down
6 changes: 2 additions & 4 deletions src/test/java/io/foldright/demo/Demo.java
Original file line number Diff line number Diff line change
@@ -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;


Expand All @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;


Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;


Expand All @@ -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);
Expand Down
Loading

0 comments on commit ea6afd7

Please sign in to comment.