Skip to content

Commit

Permalink
docs: add IntegrationDemoUsingWrapperAdapterUtils; improve javadoc 📚
Browse files Browse the repository at this point in the history
  • Loading branch information
oldratlee committed Apr 2, 2024
1 parent f2aa394 commit 295d0da
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 22 deletions.
63 changes: 61 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ The purpose of **Inspectable Wrappers** is to provide a standard for wrapper cha
- [🌰 Integration Demo](#-integration-demo)
- [the demo existed wrapper cannot be modified](#the-demo-existed-wrapper-cannot-be-modified)
- [the integration code](#the-integration-code)
- [🌰 Integration Demo using `WrapperAdapterUtils`](#-integration-demo-using-wrapperadapterutils)
- [🍼 Java API Docs](#-java-api-docs)
- [🍪 Dependency](#-dependency)

Expand All @@ -50,12 +51,12 @@ The purpose of **Inspectable Wrappers** is to provide a standard for wrapper cha
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 chain**
- The util classes:
- The utility classes:
- [`AttachableDelegate`](src/main/java/io/foldright/inspectablewrappers/utils/AttachableDelegate.java) class
provides a simple `Attachable` delegate implementation
- [`WrapperAdapterUtils`](src/main/java/io/foldright/inspectablewrappers/utils/WrapperAdapterUtils.java) class
provides utility methods for creating `WrapperAdapter` instances
without writing the boilerplate code to create a new adapter class
without writing boilerplate codes of creating new adapter classes

## 🌰 Usage Demo

Expand Down Expand Up @@ -290,6 +291,64 @@ I'm working.

> Runnable demo codes in project: [`IntegrationDemo.java`](src/test/java/io/foldright/demo/integration/IntegrationDemo.java)
## 🌰 Integration Demo using `WrapperAdapterUtils`

Uses `WrapperAdapterUtils` to create `WrapperAdapter` instances without writing the boilerplate code to create a new adapter class.

```java
public class IntegrationDemoUsingWrapperAdapterUtils {
public static void main(String[] args) {
final Executor executor = buildExecutorChain();

////////////////////////////////////////
// inspect the executor(wrapper chain)
////////////////////////////////////////

System.out.println("Is executor ExistedExecutorWrapper? " +
containsInstanceOnWrapperChain(executor, ExistedExecutorWrapper.class));
// print true
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~."

////////////////////////////////////////
// call executor(wrapper chain)
////////////////////////////////////////

System.out.println();
executor.execute(() -> System.out.println("I'm working."));
}

private static Executor buildExecutorChain() {
final Executor base = Runnable::run;
final Executor adapter = createExistedExecutorWrapperAdapter(base);
return new ChattyExecutorWrapper(adapter);
}

private static Executor createExistedExecutorWrapperAdapter(Executor base) {
final Executor existed = new ExistedExecutorWrapper(base);

Attachable<String, String> attachable = new AttachableDelegate<>();
attachable.setAttachment("adapted-existed-executor-wrapper-msg", "I'm an adapter of an existed executor which have nothing to do with ~inspectable~wrappers~.");

return WrapperAdapterUtils.createWrapperAdapter(Executor.class, base, existed, attachable);
}
}

/*
demo output:
Is executor ExistedExecutorWrapper? true
Adapted existed executor wrapper msg: I'm an adapter of an existed executor which have nothing to do with ~inspectable~wrappers~.
BlaBlaBla...
I'm an adapter of an existed executor which have nothing to do with ~inspectable~wrappers~.
I'm working.
*/
```

> Runnable demo codes in project: [`IntegrationDemoUsingWrapperAdapterUtils.java`](src/test/java/io/foldright/demo/integration/IntegrationDemoUsingWrapperAdapterUtils.java)
## 🍼 Java API Docs

The current version Java API documentation: <https://foldright.io/inspectable-wrappers/apidocs/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
* width="400" alt="Wrapper Chain contains WrapperAdapter">
* <p>
* Provided {@link io.foldright.inspectablewrappers.utils.WrapperAdapterUtils WrapperAdapterUtils}
* to create {@link WrapperAdapter} instances of the given biz interface type
* by the adaptee and underlying instance without writing boilerplate code of creating a new adapter class.
* to create {@link WrapperAdapter} instances of the given biz interface type by the underlying
* and adaptee instances without writing boilerplate codes of creating new adapter classes.
*
* @param <T> the type of instances that be wrapped
* @author Jerry Lee (oldratlee at gmail dot com)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package io.foldright.inspectablewrappers.utils;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import io.foldright.inspectablewrappers.Attachable;
import io.foldright.inspectablewrappers.Inspector;

import javax.annotation.ParametersAreNonnullByDefault;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

Expand All @@ -14,35 +15,44 @@
* A simple {@link Attachable} delegate implementation.
* <p>
* <strong>Note:</strong><br>
* As the attachments are stored in {@code hash map}(threadsafe {@link ConcurrentHashMap}), the implementation of
* the key type must meet the requirements of the {@code hash map}, which means that
* As the attachments are stored in {@code hash map}(threadsafe {@link ConcurrentHashMap}),
* the implementation of the key type must meet the requirements of the {@code hash map}, which means that
* a stable {@code hash code} and the ability to compare equality using {@code equals()} must be implemented.
*
* @author Jerry Lee (oldratlee at gmail dot com)
* @author Yang Fang (snoop dot fy at gmail dot com)
* @author Zava Xu (zava dot kid at gmail dot com)
* @see Attachable
*/
@ParametersAreNonnullByDefault
public class AttachableDelegate<K, V> implements Attachable<K, V> {
private final ConcurrentMap<K, V> attachments = new ConcurrentHashMap<>();

/**
* Sets an attachment.
*
* @param key the attachment key
* @param value the attachment value
* @throws NullPointerException if any arguments is null
*/
@Override
public void setAttachment(final K key, final V value) {
public void setAttachment(@NonNull K key, @NonNull V value) {
requireNonNull(key, "key is null");
requireNonNull(value, "value is null");
attachments.put(key, value);
}

/**
* Gets the attachment of the given key.
* Gets the attachment value for the given key.
*
* @param key the attachment key
* @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 <V>}
* @see Inspector#getAttachmentFromWrapperChain(Object, Object)
*/
@Nullable
@Override
public V getAttachment(final K key) {
public V getAttachment(@NonNull K key) {
requireNonNull(key, "key is null");
return attachments.get(key);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,32 @@

/**
* Utility class for creating {@link WrapperAdapter} instances
* without writing the boilerplate code to create a new adapter class.
* without writing boilerplate codes of creating new adapter classes.
*
* @author Jerry Lee (oldratlee at gmail dot com)
*/
@ParametersAreNonnullByDefault
public final class WrapperAdapterUtils {
/**
* Creates a {@link WrapperAdapter} instance with the given biz interface type
* by the underlying instance that be wrapped and the adapted/existed wrapper instance.
* Creates a {@link WrapperAdapter} instance of the given biz interface type by
* the underlying({@link Wrapper#unwrap()}) and adaptee({@link WrapperAdapter#adaptee()}) instances.
*
* @param <T> the type of instances that be wrapped
* @param bizInterface the class of instances that be wrapped
* @param underlying the underlying instance that be wrapped, more info see {@link Wrapper#unwrap()}
* @param adaptee the adapted/existed wrapper instance, more info see {@link WrapperAdapter#adaptee()}
* @return the new {@link WrapperAdapter} instance
* @throws IllegalArgumentException if {@code bizInterface} is not an interface,
* or {@code bizInterface} is {@link Wrapper}/{@link WrapperAdapter}/{@link Attachable},
* or underlying is not an instance of {@code bizInterface},
* or adaptee is not an instance of {@code bizInterface},
* or adaptee is an instance of {@link Wrapper}
* @throws NullPointerException if any argument is null
* @see Wrapper#unwrap()
* @see WrapperAdapter#adaptee()
*/
@NonNull
public static <T> T createWrapperAdapter(Class<T> bizInterface, T underlying, T adaptee) {
public static <T> T createWrapperAdapter(Class<? super T> bizInterface, T underlying, T adaptee) {
return createWrapperAdapter0(
requireNonNull(bizInterface, "bizInterface is null"),
requireNonNull(underlying, "underlying is null"),
Expand All @@ -47,23 +53,29 @@ public static <T> T createWrapperAdapter(Class<T> bizInterface, T underlying, T
}

/**
* Creates a {@link WrapperAdapter} instance with the given biz interface type and {@link Attachable} type
* by the underlying instance that be wrapped, the adapted/existed wrapper instance and an attachable instance.
* Creates a {@link WrapperAdapter} instance of the given biz interface type and {@link Attachable} type by
* the underlying({@link Wrapper#unwrap()}), adaptee({@link WrapperAdapter#adaptee()}) and attachable instances.
*
* @param <T> the type of instances that be wrapped
* @param bizInterface the class of instances that be wrapped
* @param underlying the underlying instance that be wrapped, more info see {@link Wrapper#unwrap()}
* @param adaptee the adapted/existed wrapper instance, more info see {@link WrapperAdapter#adaptee()}
* @param attachable the attachable instance, more info see {@link Attachable}
* @return the new {@link WrapperAdapter} instance
* @throws IllegalArgumentException if {@code bizInterface} is not an interface,
* or {@code bizInterface} is {@link Wrapper}/{@link WrapperAdapter}/{@link Attachable},
* or underlying is not an instance of {@code bizInterface},
* or adaptee is not an instance of {@code bizInterface},
* or adaptee is an instance of {@link Wrapper}
* @throws NullPointerException if any argument is null
* @see Wrapper#unwrap()
* @see WrapperAdapter#adaptee()
* @see Attachable#getAttachment(Object)
* @see Attachable#setAttachment(Object, Object)
*/
@NonNull
public static <T> T createWrapperAdapter(
Class<T> bizInterface, T underlying, T adaptee, Attachable<?, ?> attachable) {
Class<? super T> bizInterface, T underlying, T adaptee, Attachable<?, ?> attachable) {
return createWrapperAdapter0(
requireNonNull(bizInterface, "bizInterface is null"),
requireNonNull(underlying, "underlying is null"),
Expand All @@ -73,7 +85,7 @@ public static <T> T createWrapperAdapter(

@SuppressWarnings({"unchecked", "rawtypes"})
private static <T> T createWrapperAdapter0(
Class<T> bizInterface, T underlying, T adaptee, @Nullable Attachable<?, ?> attachable) {
Class<? super T> bizInterface, T underlying, T adaptee, @Nullable Attachable<?, ?> attachable) {
checkTypeRequirements(bizInterface, underlying, adaptee);

final InvocationHandler handler = (proxy, method, args) -> {
Expand Down Expand Up @@ -142,11 +154,11 @@ private WrapperAdapterUtils() {
*/
enum WrapperAdapterProxyRelatedMethod {
/**
* {@link WrapperAdapter#adaptee()}
* {@link Wrapper#unwrap()}
*/
UNWRAP(() -> Wrapper.class.getMethod("unwrap")),
/**
* {@link Wrapper#unwrap()}
* {@link WrapperAdapter#adaptee()}
*/
ADAPTEE(() -> WrapperAdapter.class.getMethod("adaptee")),
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Utils classes for {@code inspectable wrappers}.
* Utility classes for {@code inspectable wrappers}.
*
* @see io.foldright.inspectablewrappers.utils.AttachableDelegate
* @see io.foldright.inspectablewrappers.utils.WrapperAdapterUtils
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.foldright.demo.integration;

import io.foldright.demo.ChattyExecutorWrapper;
import io.foldright.inspectablewrappers.Attachable;
import io.foldright.inspectablewrappers.utils.AttachableDelegate;
import io.foldright.inspectablewrappers.utils.WrapperAdapterUtils;

import java.util.concurrent.Executor;

import static io.foldright.inspectablewrappers.Inspector.containsInstanceOnWrapperChain;
import static io.foldright.inspectablewrappers.Inspector.getAttachmentFromWrapperChain;


public class IntegrationDemoUsingWrapperAdapterUtils {
public static void main(String[] args) {
final Executor executor = buildExecutorChain();

////////////////////////////////////////
// inspect the executor(wrapper chain)
////////////////////////////////////////

System.out.println("Is executor ExistedExecutorWrapper? " +
containsInstanceOnWrapperChain(executor, ExistedExecutorWrapper.class));
// print true
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~."

////////////////////////////////////////
// call executor(wrapper chain)
////////////////////////////////////////

System.out.println();
executor.execute(() -> System.out.println("I'm working."));
}

private static Executor buildExecutorChain() {
final Executor base = Runnable::run;
final Executor adapter = createExistedExecutorWrapperAdapter(base);
return new ChattyExecutorWrapper(adapter);
}

private static Executor createExistedExecutorWrapperAdapter(Executor base) {
final Executor existed = new ExistedExecutorWrapper(base);

Attachable<String, String> attachable = new AttachableDelegate<>();
attachable.setAttachment("adapted-existed-executor-wrapper-msg", "I'm an adapter of an existed executor which have nothing to do with ~inspectable~wrappers~.");

return WrapperAdapterUtils.createWrapperAdapter(Executor.class, base, existed, attachable);
}
}

/*
demo output:
Is executor ExistedExecutorWrapper? true
Adapted existed executor wrapper msg: I'm an adapter of an existed executor which have nothing to do with ~inspectable~wrappers~.
BlaBlaBla...
I'm an adapter of an existed executor which have nothing to do with ~inspectable~wrappers~.
I'm working.
*/

0 comments on commit 295d0da

Please sign in to comment.