Skip to content

Commit 72e4ad1

Browse files
committed
refactor: extract Inspector from Wrapper
1 parent 9f40e2c commit 72e4ad1

File tree

8 files changed

+194
-166
lines changed

8 files changed

+194
-166
lines changed

src/main/java/io/foldright/inspectablewrappers/Attachable.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* provide the attachment storage ability.
1010
* <p>
1111
* Retrieve the attachment from wrapper chain(wrapper instances implement interface {@link Wrapper})
12-
* by static method {@link Wrapper#getAttachment(Object, Object)}.
12+
* by static method {@link Inspector#getAttachment(Object, Object)}.
1313
* <p>
1414
* Provide {@link io.foldright.inspectablewrappers.utils.AttachableDelegate AttachableDelegate}
1515
* as a simple delegate implementation.
@@ -18,7 +18,7 @@
1818
* @param <V> the value type to be stored
1919
* @author Jerry Lee (oldratlee at gmail dot com)
2020
* @author Yang Fang (snoop dot fy at gmail dot com)
21-
* @see Wrapper#getAttachment(Object, Object)
21+
* @see Inspector#getAttachment(Object, Object)
2222
* @see io.foldright.inspectablewrappers.utils.AttachableDelegate
2323
*/
2424
public interface Attachable<K, V> {
@@ -38,7 +38,7 @@ public interface Attachable<K, V> {
3838
* @return return the attachment value, or {@code null} if contains no attachment for the key
3939
* @throws NullPointerException if key argument is null
4040
* @throws ClassCastException if the return value is not type {@code <V>}
41-
* @see Wrapper#getAttachment(Object, Object)
41+
* @see Inspector#getAttachment(Object, Object)
4242
*/
4343
@Nullable
4444
V getAttachment(@NonNull K key);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package io.foldright.inspectablewrappers;
2+
3+
import edu.umd.cs.findbugs.annotations.NonNull;
4+
import edu.umd.cs.findbugs.annotations.Nullable;
5+
6+
import java.util.Optional;
7+
import java.util.function.Function;
8+
import java.util.function.Predicate;
9+
10+
import static io.foldright.inspectablewrappers.InternalUtils.unwrapNonNull;
11+
import static java.util.Objects.requireNonNull;
12+
13+
14+
/**
15+
* This {@code Inspector} is used to inspect wrapper implementors:
16+
* <ul>
17+
* <li>
18+
* Reports whether any wrapper on the wrapper chain matches the given type by static method {@link #isInstanceOf(Object, Class)}
19+
* </li>
20+
* <li>
21+
* Retrieve the attachment from wrapper chain(wrapper instances implement interface {@link Wrapper})
22+
* by static method {@link #getAttachment(Object, Object)}.
23+
* </li>
24+
* </ul>
25+
*
26+
* @see Wrapper
27+
*/
28+
public final class Inspector {
29+
30+
// no need to create instance at all
31+
private Inspector() {
32+
}
33+
34+
/**
35+
* Reports whether any wrapper on the wrapper chain matches the given type.
36+
* <p>
37+
* The wrapper chain consists of wrapper itself, followed by the wrappers
38+
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
39+
*
40+
* @param wrapper wrapper instance/wrapper chain
41+
* @param clazz target type
42+
* @param <W> the type of instances that be wrapped
43+
* @return return {@code false} if no wrapper on the wrapper chain matches the given type,
44+
* otherwise return {@code true}
45+
* @throws NullPointerException if any arguments is null or any wrapper {{@link Wrapper#unwrap()}} returns null
46+
* @see WrapperAdapter#adaptee()
47+
*/
48+
public static <W> boolean isInstanceOf(final W wrapper, final Class<?> clazz) {
49+
requireNonNull(wrapper, "wrapper is null");
50+
requireNonNull(clazz, "clazz is null");
51+
return inspect(wrapper, w -> clazz.isAssignableFrom(w.getClass()));
52+
}
53+
54+
/**
55+
* Reports whether any wrapper on the wrapper chain satisfy the given {@code predicate}.
56+
* <p>
57+
* The wrapper chain consists of wrapper itself, followed by the wrappers
58+
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
59+
*
60+
* @param wrapper wrapper instance/wrapper chain
61+
* @param predicate inspect logic
62+
* @param <W> the type of instances that be wrapped
63+
* @return return {@code false} if no wrapper on the wrapper chain satisfy the given {@code predicate},
64+
* otherwise return {@code true}
65+
* @throws NullPointerException if any arguments is null or any wrapper {{@link Wrapper#unwrap()}} returns null
66+
*/
67+
static <W> boolean inspect(final W wrapper, final Predicate<? super W> predicate) {
68+
requireNonNull(wrapper, "wrapper is null");
69+
requireNonNull(predicate, "predicate is null");
70+
return travel(wrapper, w -> {
71+
if (predicate.test(w)) return Optional.of(true);
72+
else return Optional.empty();
73+
}).orElse(false);
74+
}
75+
76+
/**
77+
* Retrieves the attachment of wrapper of given key on the wrapper chain
78+
* by calling {@link Attachable#getAttachment(Object)}.
79+
* <p>
80+
* The wrapper chain consists of wrapper itself, followed by the wrappers
81+
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
82+
* <p>
83+
* If the key exists in multiple wrappers, outer wrapper win.
84+
*
85+
* @param wrapper wrapper instance
86+
* @param key the attachment key
87+
* @param <W> the type of instances that be wrapped
88+
* @param <K> the type of attachment key
89+
* @param <V> the type of attachment value
90+
* @return the attachment value of wrapper of given key on the wrapper chain,
91+
* or null if the attachment is absent
92+
* @throws NullPointerException if any arguments is null or any wrapper {{@link Wrapper#unwrap()}} returns null
93+
* @throws ClassCastException if the return value is not type {@code <V>}
94+
* @see Attachable#getAttachment(Object)
95+
*/
96+
@Nullable
97+
@SuppressWarnings("unchecked")
98+
public static <W, K, V> V getAttachment(final W wrapper, final K key) {
99+
requireNonNull(wrapper, "wrapper is null");
100+
requireNonNull(key, "key is null");
101+
return travel(wrapper, w -> {
102+
if (w instanceof Attachable) {
103+
V value = ((Attachable<K, V>) w).getAttachment(key);
104+
return Optional.ofNullable(value);
105+
} else {
106+
return Optional.empty();
107+
}
108+
}).orElse(null);
109+
}
110+
111+
112+
/**
113+
* Traverses the wrapper chain, and apply the given {@code process} function to each wrapper,
114+
* and returns the first non-empty({@link Optional#empty()}) result of the process function,
115+
* otherwise returns {@link Optional#empty()}.
116+
* <p>
117+
* The wrapper chain consists of wrapper itself, followed by the wrappers
118+
* obtained by repeatedly calling {@link Wrapper#unwrap()}.
119+
*
120+
* @param wrapper wrapper instance
121+
* @param process process function
122+
* @param <W> the type of instances that be wrapped
123+
* @param <T> the return data type of process function
124+
* @return the first non-empty({@link Optional#empty()}) result of the process function,
125+
* otherwise returns {@link Optional#empty()}.
126+
* @throws NullPointerException if any arguments is null or any wrapper {{@link Wrapper#unwrap()}} returns null
127+
* @throws IllegalStateException if the adaptee of {@link WrapperAdapter} is a wrapper instance,
128+
* the use of WrapperAdapter is unnecessary!
129+
*/
130+
@NonNull
131+
@SuppressWarnings("unchecked")
132+
static <W, T> Optional<T> travel(final W wrapper, final Function<W, Optional<T>> process) {
133+
requireNonNull(wrapper, "wrapper is null");
134+
requireNonNull(process, "process is null");
135+
136+
Object w = wrapper;
137+
while (true) {
138+
// process the instance on wrapper chain
139+
Optional<T> result = process.apply((W) w);
140+
if (result.isPresent()) return result;
141+
142+
// also process the adaptee if it's a WrapperAdapter
143+
if (w instanceof WrapperAdapter) {
144+
final Object adaptee = ((WrapperAdapter<?>) w).adaptee();
145+
if (adaptee instanceof Wrapper) {
146+
throw new IllegalStateException("adaptee(" + adaptee.getClass().getName() + ") of WrapperAdapter(" + w.getClass().getName() + ") is a wrapper instance, the use of WrapperAdapter is unnecessary!");
147+
}
148+
149+
Optional<T> r = process.apply((W) adaptee);
150+
if (r.isPresent()) return r;
151+
}
152+
153+
if (!(w instanceof Wrapper)) return Optional.empty();
154+
w = unwrapNonNull(w);
155+
}
156+
}
157+
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
package io.foldright.inspectablewrappers;
22

33
import edu.umd.cs.findbugs.annotations.NonNull;
4-
import edu.umd.cs.findbugs.annotations.Nullable;
54

65
import javax.annotation.ParametersAreNonnullByDefault;
7-
import java.util.Optional;
8-
import java.util.function.Function;
9-
import java.util.function.Predicate;
10-
11-
import static io.foldright.inspectablewrappers.InternalUtils.unwrapNonNull;
12-
import static java.util.Objects.requireNonNull;
136

147

158
/**
@@ -20,6 +13,7 @@
2013
* @author Jerry Lee (oldratlee at gmail dot com)
2114
* @see Attachable
2215
* @see WrapperAdapter
16+
* @see Inspector
2317
*/
2418
@ParametersAreNonnullByDefault
2519
public interface Wrapper<T> {
@@ -31,128 +25,5 @@ public interface Wrapper<T> {
3125
@NonNull
3226
T unwrap();
3327

34-
/**
35-
* Reports whether any wrapper on the wrapper chain matches the given type.
36-
* <p>
37-
* The wrapper chain consists of wrapper itself, followed by the wrappers
38-
* obtained by repeatedly calling {@link #unwrap()}.
39-
*
40-
* @param wrapper wrapper instance/wrapper chain
41-
* @param clazz target type
42-
* @param <W> the type of instances that be wrapped
43-
* @return return {@code false} if no wrapper on the wrapper chain matches the given type,
44-
* otherwise return {@code true}
45-
* @throws NullPointerException if any arguments is null or any wrapper {{@link #unwrap()}} returns null
46-
* @see WrapperAdapter#adaptee()
47-
*/
48-
static <W> boolean isInstanceOf(final W wrapper, final Class<?> clazz) {
49-
requireNonNull(wrapper, "wrapper is null");
50-
requireNonNull(clazz, "clazz is null");
51-
return inspect(wrapper, w -> clazz.isAssignableFrom(w.getClass()));
52-
}
53-
54-
/**
55-
* Reports whether any wrapper on the wrapper chain satisfy the given {@code predicate}.
56-
* <p>
57-
* The wrapper chain consists of wrapper itself, followed by the wrappers
58-
* obtained by repeatedly calling {@link #unwrap()}.
59-
*
60-
* @param wrapper wrapper instance/wrapper chain
61-
* @param predicate inspect logic
62-
* @param <W> the type of instances that be wrapped
63-
* @return return {@code false} if no wrapper on the wrapper chain satisfy the given {@code predicate},
64-
* otherwise return {@code true}
65-
* @throws NullPointerException if any arguments is null or any wrapper {{@link #unwrap()}} returns null
66-
*/
67-
static <W> boolean inspect(final W wrapper, final Predicate<? super W> predicate) {
68-
requireNonNull(wrapper, "wrapper is null");
69-
requireNonNull(predicate, "predicate is null");
70-
return travel(wrapper, w -> {
71-
if (predicate.test(w)) return Optional.of(true);
72-
else return Optional.empty();
73-
}).orElse(false);
74-
}
75-
76-
/**
77-
* Retrieves the attachment of wrapper of given key on the wrapper chain
78-
* by calling {@link Attachable#getAttachment(Object)}.
79-
* <p>
80-
* The wrapper chain consists of wrapper itself, followed by the wrappers
81-
* obtained by repeatedly calling {@link #unwrap()}.
82-
* <p>
83-
* If the key exists in multiple wrappers, outer wrapper win.
84-
*
85-
* @param wrapper wrapper instance
86-
* @param key the attachment key
87-
* @param <W> the type of instances that be wrapped
88-
* @param <K> the type of attachment key
89-
* @param <V> the type of attachment value
90-
* @return the attachment value of wrapper of given key on the wrapper chain,
91-
* or null if the attachment is absent
92-
* @throws NullPointerException if any arguments is null or any wrapper {{@link #unwrap()}} returns null
93-
* @throws ClassCastException if the return value is not type {@code <V>}
94-
* @see Attachable#getAttachment(Object)
95-
*/
96-
@Nullable
97-
@SuppressWarnings("unchecked")
98-
static <W, K, V> V getAttachment(final W wrapper, final K key) {
99-
requireNonNull(wrapper, "wrapper is null");
100-
requireNonNull(key, "key is null");
101-
return travel(wrapper, w -> {
102-
if (w instanceof Attachable) {
103-
V value = ((Attachable<K, V>) w).getAttachment(key);
104-
return Optional.ofNullable(value);
105-
} else {
106-
return Optional.empty();
107-
}
108-
}).orElse(null);
109-
}
110-
111-
/**
112-
* Traverses the wrapper chain, and apply the given {@code process} function to each wrapper,
113-
* and returns the first non-empty({@link Optional#empty()}) result of the process function,
114-
* otherwise returns {@link Optional#empty()}.
115-
* <p>
116-
* The wrapper chain consists of wrapper itself, followed by the wrappers
117-
* obtained by repeatedly calling {@link #unwrap()}.
118-
*
119-
* @param wrapper wrapper instance
120-
* @param process process function
121-
* @param <W> the type of instances that be wrapped
122-
* @param <T> the return data type of process function
123-
* @return the first non-empty({@link Optional#empty()}) result of the process function,
124-
* otherwise returns {@link Optional#empty()}.
125-
* @throws NullPointerException if any arguments is null or any wrapper {{@link #unwrap()}} returns null
126-
* @throws IllegalStateException if the adaptee of {@link WrapperAdapter} is a wrapper instance,
127-
* the use of WrapperAdapter is unnecessary!
128-
*/
129-
@NonNull
130-
@SuppressWarnings("unchecked")
131-
static <W, T> Optional<T> travel(final W wrapper, final Function<W, Optional<T>> process) {
132-
requireNonNull(wrapper, "wrapper is null");
133-
requireNonNull(process, "process is null");
134-
135-
Object w = wrapper;
136-
while (true) {
137-
// process the instance on wrapper chain
138-
Optional<T> result = process.apply((W) w);
139-
if (result.isPresent()) return result;
140-
141-
// also process the adaptee if it's a WrapperAdapter
142-
if (w instanceof WrapperAdapter) {
143-
final Object adaptee = ((WrapperAdapter<?>) w).adaptee();
144-
if (adaptee instanceof Wrapper) {
145-
throw new IllegalStateException("adaptee(" + adaptee.getClass().getName() +
146-
") of WrapperAdapter(" + w.getClass().getName() +
147-
") is a wrapper instance, the use of WrapperAdapter is unnecessary!");
148-
}
149-
150-
Optional<T> r = process.apply((W) adaptee);
151-
if (r.isPresent()) return r;
152-
}
15328

154-
if (!(w instanceof Wrapper)) return Optional.empty();
155-
w = unwrapNonNull(w);
156-
}
157-
}
15829
}

src/main/java/io/foldright/inspectablewrappers/WrapperAdapter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public interface WrapperAdapter<T> extends Wrapper<T> {
1717
* Returns the adapted/existed wrapper.
1818
*
1919
* @return the adapted wrapper.
20-
* @see Wrapper#isInstanceOf(Object, Class)
20+
* @see Inspector#isInstanceOf(Object, Class)
2121
*/
2222
@NonNull
2323
T adaptee();

src/test/java/io/foldright/demo/Demo.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.foldright.demo;
22

3-
import io.foldright.inspectablewrappers.Wrapper;
3+
import io.foldright.inspectablewrappers.Inspector;
44

55
import java.util.concurrent.Executor;
66

@@ -14,10 +14,10 @@ public static void main(String[] args) {
1414
////////////////////////////////////////
1515

1616
System.out.println("Is executor lazy? " +
17-
Wrapper.isInstanceOf(executor, LazyExecutorWrapper.class));
17+
Inspector.isInstanceOf(executor, LazyExecutorWrapper.class));
1818
// print true
1919

20-
String busy = Wrapper.getAttachment(executor, "busy");
20+
String busy = Inspector.getAttachment(executor, "busy");
2121
System.out.println("Is executor busy? " + busy);
2222
// print "very, very busy!"
2323

src/test/java/io/foldright/demo/integration/IntegrationDemo.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault;
55
import io.foldright.demo.ChattyExecutorWrapper;
66
import io.foldright.inspectablewrappers.Attachable;
7-
import io.foldright.inspectablewrappers.Wrapper;
7+
import io.foldright.inspectablewrappers.Inspector;
88
import io.foldright.inspectablewrappers.WrapperAdapter;
99
import io.foldright.inspectablewrappers.utils.AttachableDelegate;
1010

@@ -23,9 +23,9 @@ public static void main(String[] args) {
2323
////////////////////////////////////////
2424

2525
System.out.println("Is executor ExistedExecutorWrapper? " +
26-
Wrapper.isInstanceOf(executor, ExistedExecutorWrapper.class));
26+
Inspector.isInstanceOf(executor, ExistedExecutorWrapper.class));
2727
// print true
28-
String adaptAttachment = Wrapper.getAttachment(executor, "adapted-existed-executor-wrapper-msg");
28+
String adaptAttachment = Inspector.getAttachment(executor, "adapted-existed-executor-wrapper-msg");
2929
System.out.println("Adapted existed executor wrapper msg: " + adaptAttachment);
3030
// print "I'm an adapter of an existed executor which have nothing to do with ~inspectable~wrappers~."
3131

0 commit comments

Comments
 (0)