UpdateInjection.java
/**
* Copyright (C) 2022 Christopher J. Stehno
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.cjstehno.testthings.inject;
import lombok.RequiredArgsConstructor;
import lombok.val;
import java.util.function.Function;
import static java.util.Locale.ROOT;
import static lombok.AccessLevel.PACKAGE;
import static org.junit.platform.commons.support.HierarchyTraversalMode.TOP_DOWN;
import static org.junit.platform.commons.support.ModifierSupport.isNotStatic;
import static org.junit.platform.commons.support.ReflectionSupport.findFields;
import static org.junit.platform.commons.support.ReflectionSupport.findMethods;
/**
* An Injection implementation which injects the value returned by a function, which is passed the current field
* value. Optionally, the setter and getter methods may be used rather than the direct field access.
*/
@RequiredArgsConstructor(access = PACKAGE)
class UpdateInjection implements Injection {
private final String name;
private final Function<Object, Object> updater;
private final boolean preferSetter;
private final boolean preferGetter;
/**
* Injects the configured value into the given instance.
*
* @param instance the instance the value is being injected into
* @throws ReflectiveOperationException if there is a problem injecting the value
*/
@Override
public void injectInto(final Object instance) throws ReflectiveOperationException {
val currentValue = resolveCurrentValue(instance, name, preferGetter);
// transform the value
val updatedValue = updater.apply(currentValue);
// update the value
new SetInjection(name, updatedValue, preferSetter).injectInto(instance);
}
protected static Object resolveCurrentValue(final Object instance, final String name, final boolean preferGetter) throws ReflectiveOperationException {
if (preferGetter) {
// try to use the getter (if it exists)
val methodName = "get" + name.substring(0, 1).toUpperCase(ROOT) + name.substring(1);
val methods = findMethods(
instance.getClass(),
m -> m.getName().equals(methodName) && m.getParameterCount() == 0 && isNotStatic(m),
TOP_DOWN
);
if (!methods.isEmpty()) {
val method = methods.get(0);
method.setAccessible(true);
return method.invoke(instance);
}
}
// try to use the field
return findFields(instance.getClass(), f -> f.getName().equals(name) && isNotStatic(f), TOP_DOWN).stream()
.peek(f -> f.setAccessible(true))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("Unable to resolve current value for '" + name + "'"))
.get(instance);
}
}