-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove StandardMethodResolver and refactor method resolution
Eliminated `StandardMethodResolver.java` and adapted the method resolution logic by creating a more modular and reusable structure with `Invokers` and `ReflectionExecutor`. Updated `DocxStamper` to integrate the new mechanism, simplifying the handling of method executors.
- Loading branch information
1 parent
f36cf62
commit c5da6ac
Showing
6 changed files
with
162 additions
and
140 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 17 additions & 27 deletions
44
engine/src/main/java/pro/verron/officestamper/core/Invoker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,29 @@ | ||
package pro.verron.officestamper.core; | ||
|
||
import org.springframework.expression.AccessException; | ||
import org.springframework.expression.EvaluationContext; | ||
import org.springframework.expression.MethodExecutor; | ||
import org.springframework.expression.TypedValue; | ||
import org.springframework.lang.NonNull; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
import static java.util.Arrays.asList; | ||
|
||
|
||
/** | ||
* The Invoker class encapsulates an object and a method to be invoked on that object. It provides a way to execute | ||
* The Invoker class encapsulates an object and a method to be invoked on that object. It provides a way to | ||
* execute | ||
* the specified method with given arguments within a certain evaluation context. | ||
* | ||
* @param object the target object on which the method is to be invoked | ||
* @param method the method to be invoked on the target object | ||
*/ | ||
public record Invoker(Object object, Method method) | ||
implements MethodExecutor { | ||
@Override @NonNull | ||
public TypedValue execute( | ||
@NonNull EvaluationContext context, | ||
@NonNull Object target, | ||
@NonNull Object... arguments | ||
) | ||
throws AccessException { | ||
try { | ||
var value = method.invoke(object, arguments); | ||
return new TypedValue(value); | ||
} catch (InvocationTargetException | IllegalAccessException e) { | ||
var message = "Failed to invoke method %s with arguments [%s] from object %s" | ||
.formatted(method, Arrays.toString(arguments), object); | ||
throw new AccessException(message, e); | ||
} | ||
public record Invoker(String name, Invokers.Args args, MethodExecutor executor) { | ||
|
||
/** | ||
* @param obj the target object on which the method is to be invoked | ||
* @param method the method to be invoked on the target object | ||
*/ | ||
public Invoker(Object obj, Method method) { | ||
this(method.getName(), asList(method.getParameterTypes()), new ReflectionExecutor(obj, method)); | ||
} | ||
|
||
public Invoker(String name, List<Class<?>> args, MethodExecutor executor) { | ||
this(name, new Invokers.Args(args), executor); | ||
} | ||
} |
91 changes: 91 additions & 0 deletions
91
engine/src/main/java/pro/verron/officestamper/core/Invokers.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package pro.verron.officestamper.core; | ||
|
||
import org.springframework.core.convert.TypeDescriptor; | ||
import org.springframework.expression.EvaluationContext; | ||
import org.springframework.expression.MethodExecutor; | ||
import org.springframework.expression.MethodResolver; | ||
import org.springframework.lang.NonNull; | ||
|
||
import java.util.ArrayDeque; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Map.Entry; | ||
import java.util.stream.Stream; | ||
|
||
import static java.util.Arrays.stream; | ||
import static java.util.Collections.emptyMap; | ||
import static java.util.stream.Collectors.groupingBy; | ||
import static java.util.stream.Collectors.toMap; | ||
|
||
/** | ||
* Resolves methods that are used as expression functions or comment processors. | ||
* | ||
* @author Joseph Verron | ||
* @version ${version} | ||
* @since 1.6.2 | ||
*/ | ||
public class Invokers | ||
implements MethodResolver { | ||
private final Map<String, Map<Args, MethodExecutor>> map; | ||
|
||
public Invokers(Stream<Invoker> invokerStream) { | ||
map = invokerStream.collect(groupingBy( | ||
Invoker::name, | ||
toMap(Invoker::args, Invoker::executor) | ||
)); | ||
} | ||
|
||
static Stream<Invoker> streamInvokers(Map<Class<?>, ?> interfaces2implementations) { | ||
return interfaces2implementations | ||
.entrySet() | ||
.stream() | ||
.flatMap(Invokers::streamInvokers); | ||
} | ||
|
||
private static Stream<Invoker> streamInvokers(Entry<Class<?>, ?> interface2implementation) { | ||
return streamInvokers(interface2implementation.getKey(), interface2implementation.getValue()); | ||
} | ||
|
||
private static Stream<Invoker> streamInvokers(Class<?> key, Object obj) { | ||
return stream(key.getDeclaredMethods()) | ||
.map(method -> new Invoker(obj, method)); | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
@Override | ||
public MethodExecutor resolve( | ||
@NonNull EvaluationContext context, | ||
@NonNull Object targetObject, | ||
@NonNull String name, | ||
@NonNull List<TypeDescriptor> argumentTypes | ||
) { | ||
List<Class<?>> argumentClasses = new ArrayList<>(); | ||
argumentTypes.forEach(at -> argumentClasses.add(at.getType())); | ||
return map.getOrDefault(name, emptyMap()) | ||
.entrySet() | ||
.stream() | ||
.filter(entry -> entry.getKey() | ||
.validate(argumentClasses)) | ||
.map(Entry::getValue) | ||
.findFirst() | ||
.orElse(null); | ||
} | ||
|
||
public record Args(List<Class<?>> sourceTypes) { | ||
public boolean validate(List<Class<?>> searchedTypes) { | ||
if (searchedTypes.size() != sourceTypes.size()) | ||
return false; | ||
|
||
var sourceTypesQ = new ArrayDeque<>(sourceTypes); | ||
var searchedTypesQ = new ArrayDeque<>(searchedTypes); | ||
var valid = true; | ||
while (!sourceTypesQ.isEmpty() && valid) { | ||
Class<?> parameterType = sourceTypesQ.remove(); | ||
Class<?> searchedType = searchedTypesQ.remove(); | ||
valid = parameterType.isAssignableFrom(searchedType); | ||
} | ||
return valid; | ||
} | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
engine/src/main/java/pro/verron/officestamper/core/ReflectionExecutor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package pro.verron.officestamper.core; | ||
|
||
import org.springframework.expression.AccessException; | ||
import org.springframework.expression.EvaluationContext; | ||
import org.springframework.expression.MethodExecutor; | ||
import org.springframework.expression.TypedValue; | ||
import org.springframework.lang.NonNull; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import java.util.Arrays; | ||
|
||
public record ReflectionExecutor(Object object, Method method) | ||
implements MethodExecutor { | ||
|
||
@Override @NonNull | ||
public TypedValue execute( | ||
@NonNull EvaluationContext context, | ||
@NonNull Object target, | ||
@NonNull Object... arguments | ||
) | ||
throws AccessException { | ||
try { | ||
var value = method.invoke(object, arguments); | ||
return new TypedValue(value); | ||
} catch (InvocationTargetException | IllegalAccessException e) { | ||
var message = "Failed to invoke method %s with arguments [%s] from object %s" | ||
.formatted(method, Arrays.toString(arguments), object); | ||
throw new AccessException(message, e); | ||
} | ||
} | ||
} |
93 changes: 0 additions & 93 deletions
93
engine/src/main/java/pro/verron/officestamper/core/StandardMethodResolver.java
This file was deleted.
Oops, something went wrong.