|
|
@@ -15,7 +15,7 @@ import java.util.function.Function;
|
|
|
* It should select best matching overload of method.
|
|
|
* Optionally, class offers double-dispatch, basing on argument and return type.
|
|
|
*
|
|
|
- * It can be used to implement rather sophisticated casting.
|
|
|
+ * It can be used for example to implement sophisticated casting.
|
|
|
*/
|
|
|
public class DynamicInvoker {
|
|
|
|
|
|
@@ -26,33 +26,81 @@ public class DynamicInvoker {
|
|
|
private final boolean passIdent;
|
|
|
|
|
|
private DynamicInvoker(Builder src) {
|
|
|
- this.list = src.list;
|
|
|
+ this.list = src.map.toList();
|
|
|
this.passNull = src.passNull;
|
|
|
this.passIdent = src.passIdent;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Every invoker must be constructed using builder, before use.
|
|
|
+ * You can create new instance using "new"
|
|
|
+ *
|
|
|
+ * @return builder
|
|
|
+ */
|
|
|
public static Builder builder() {
|
|
|
return new Builder();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Executes best matching method for provided argument.
|
|
|
+ * Best match means accepting most specific type compatible with value.
|
|
|
+ *
|
|
|
+ * @param value value
|
|
|
+ * @return result
|
|
|
+ */
|
|
|
public Object call(Object value) {
|
|
|
- Function<Object, Object> func = match(value)
|
|
|
- .orElseThrow(() -> new IllegalArgumentException("No method found for: " + IClass.typefor(value)));
|
|
|
- return func.apply(value);
|
|
|
+ return match(value)
|
|
|
+ .orElseThrow(() -> new IllegalArgumentException("No method found for: " + IClass.typefor(value)))
|
|
|
+ .apply(value);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Executes best matching method for provided argument.
|
|
|
+ * Best match means accepting most specific type compatible with value
|
|
|
+ * and returning type compatible with expected result.
|
|
|
+ *
|
|
|
+ * Strictly speaking:
|
|
|
+ * argument is used to find closest match, but result is used to filter out invalid return types.
|
|
|
+ *
|
|
|
+ * @param returns returns
|
|
|
+ * @param value value
|
|
|
+ * @param <T> T
|
|
|
+ * @param <R> R
|
|
|
+ * @return result
|
|
|
+ */
|
|
|
public <T,R> R call(Class<R> returns, T value) {
|
|
|
- Function<T, R> func = match(returns, value)
|
|
|
- .orElseThrow(() -> new IllegalArgumentException("No method found for: " + IClass.typefor(value) + " =>" + ClassUtils.box(returns)));
|
|
|
- return func.apply(value);
|
|
|
+ return match(returns, value)
|
|
|
+ .orElseThrow(() -> new IllegalArgumentException("No method found for: " + IClass.typefor(value) + " =>" + ClassUtils.box(returns)))
|
|
|
+ .apply(value);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Executes best matching method for provided argument.
|
|
|
+ * Best match means accepting most specific type compatible with value
|
|
|
+ * and returning type compatible with expected result.
|
|
|
+ *
|
|
|
+ * Strictly speaking:
|
|
|
+ * argument is used to find closest match, but result is used to filter out invalid return types.
|
|
|
+ *
|
|
|
+ * @param returns returns
|
|
|
+ * @param value value
|
|
|
+ * @param <T> T
|
|
|
+ * @param <R> R
|
|
|
+ * @return result
|
|
|
+ */
|
|
|
public <T,R> R call(IClass<R> returns, T value) {
|
|
|
- Function<T, R> func = match(returns, value)
|
|
|
- .orElseThrow(() -> new IllegalArgumentException("No method found for: " + IClass.typefor(value) + " => " + ClassUtils.box(returns)));
|
|
|
- return func.apply(value);
|
|
|
+ return match(returns, value)
|
|
|
+ .orElseThrow(() -> new IllegalArgumentException("No method found for: " + IClass.typefor(value) + " => " + ClassUtils.box(returns)))
|
|
|
+ .apply(value);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Find best matching method and returns it as a "Function"
|
|
|
+ * Best match means accepting most specific type compatible with value.
|
|
|
+ *
|
|
|
+ * @param value value
|
|
|
+ * @return function
|
|
|
+ */
|
|
|
@SuppressWarnings("unchecked")
|
|
|
public Optional<Function<Object, Object>> match(Object value) {
|
|
|
if(value == null && passNull) {
|
|
|
@@ -66,10 +114,38 @@ public class DynamicInvoker {
|
|
|
return Optional.empty();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Find best matching method and returns it as a "Function"
|
|
|
+ * Best match means accepting most specific type compatible with value
|
|
|
+ * and returning type compatible with expected result.
|
|
|
+ *
|
|
|
+ * Strictly speaking:
|
|
|
+ * argument is used to find closest match, but result is used to filter out invalid return types.
|
|
|
+ *
|
|
|
+ * @param returns returns
|
|
|
+ * @param value value
|
|
|
+ * @param <T> T
|
|
|
+ * @param <R> R
|
|
|
+ * @return function
|
|
|
+ */
|
|
|
public <T, R> Optional<Function<T,R>> match(Class<R> returns, T value) {
|
|
|
return match(IClass.typeinfo(returns), value);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Find best matching method and returns it as a "Function"
|
|
|
+ * Best match means accepting most specific type compatible with value
|
|
|
+ * and returning type compatible with expected result.
|
|
|
+ *
|
|
|
+ * Strictly speaking:
|
|
|
+ * argument is used to find closest match, but result is used to filter out invalid return types.
|
|
|
+ *
|
|
|
+ * @param returns returns
|
|
|
+ * @param value value
|
|
|
+ * @param <T> T
|
|
|
+ * @param <R> R
|
|
|
+ * @return function
|
|
|
+ */
|
|
|
@SuppressWarnings("unchecked")
|
|
|
public <T, R> Optional<Function<T,R>> match(IClass<R> returns, T value) {
|
|
|
if(value == null && passNull) {
|
|
|
@@ -92,38 +168,88 @@ public class DynamicInvoker {
|
|
|
return Optional.empty();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Builder pattern for DynamicInvoker
|
|
|
+ */
|
|
|
public static final class Builder {
|
|
|
|
|
|
private final TypeList<RFunction> map = new TypeList<>();
|
|
|
|
|
|
- private final List<RFunction> list = new ArrayList<>();
|
|
|
-
|
|
|
private boolean passNull = true;
|
|
|
|
|
|
private boolean passIdent = true;
|
|
|
|
|
|
+ /**
|
|
|
+ * Configures invoker to return input argument without any calls, if
|
|
|
+ * input type and return type are the same.
|
|
|
+ *
|
|
|
+ * @param passIdent passIdent
|
|
|
+ * @return builder
|
|
|
+ */
|
|
|
public Builder passIdent(boolean passIdent) {
|
|
|
this.passIdent = passIdent;
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Configures invoker to return null without any calls, if input value is "null"
|
|
|
+ *
|
|
|
+ * @param passNull passNull
|
|
|
+ * @return builder
|
|
|
+ */
|
|
|
public Builder passNull(boolean passNull) {
|
|
|
this.passNull = passNull;
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Inserts provided function for specified types of input and result.
|
|
|
+ *
|
|
|
+ * @param argument argument
|
|
|
+ * @param returns returns
|
|
|
+ * @param function function
|
|
|
+ * @param <T> T
|
|
|
+ * @param <R> R
|
|
|
+ * @return builder
|
|
|
+ */
|
|
|
public <T,R> Builder append(Class<T> argument, Class<R> returns, Function<T, R> function) {
|
|
|
return append0(argument, IClass.typeinfo(returns), function);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Inserts provided function for specified types of input and result.
|
|
|
+ *
|
|
|
+ * @param argument argument
|
|
|
+ * @param returns returns
|
|
|
+ * @param function function
|
|
|
+ * @param <T> T
|
|
|
+ * @param <R> R
|
|
|
+ * @return builder
|
|
|
+ */
|
|
|
public <T,R> Builder append(Class<T> argument, IClass<R> returns, Function<T, R> function) {
|
|
|
return append0(argument, returns, function);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Inserts provided function for specified type of input.
|
|
|
+ * Type of result is assumed to be "void".
|
|
|
+ *
|
|
|
+ * @param argument argument
|
|
|
+ * @param consumer consumer
|
|
|
+ * @param <T> T
|
|
|
+ * @return builder
|
|
|
+ */
|
|
|
public <T> Builder append(Class<T> argument, Consumer<T> consumer) {
|
|
|
return append(argument, IClass.VOID, v -> { consumer.accept(v); return null; });
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Scans provided type for static methods annotated with "DynamicDispatch" and
|
|
|
+ * inserts them into invoker.
|
|
|
+ *
|
|
|
+ * @param helper helper
|
|
|
+ * @return builder
|
|
|
+ */
|
|
|
public Builder appendHelper(Class<?> helper) {
|
|
|
IClass.typeinfo(helper).methods()
|
|
|
.require(IAttribute.STATIC)
|
|
|
@@ -139,6 +265,13 @@ public class DynamicInvoker {
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Scans provided object for non-static methods annotated with "DynamicDispatch" and
|
|
|
+ * inserts them into invoker. All methods will bind "this" to "delegate".
|
|
|
+ *
|
|
|
+ * @param delegate delegate
|
|
|
+ * @return builder
|
|
|
+ */
|
|
|
public Builder appendDelegate(Object delegate) {
|
|
|
IClass.typefor(delegate).methods()
|
|
|
.require(IAttribute.ANY)
|
|
|
@@ -163,8 +296,12 @@ public class DynamicInvoker {
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Creates new configured invoker
|
|
|
+ *
|
|
|
+ * @return involer
|
|
|
+ */
|
|
|
public DynamicInvoker build() {
|
|
|
- map.toList(list);
|
|
|
return new DynamicInvoker(this);
|
|
|
}
|
|
|
|
|
|
@@ -189,8 +326,13 @@ public class DynamicInvoker {
|
|
|
|
|
|
private final Node<V> root = new Node<>(null, null, null);
|
|
|
|
|
|
- public void toList(List<V> target) {
|
|
|
+ public List<V> toList() {
|
|
|
+ return toList(new ArrayList<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<V> toList(List<V> target) {
|
|
|
toList(target, root);
|
|
|
+ return target;
|
|
|
}
|
|
|
|
|
|
private void toList(List<V> target, Node<V> node) {
|