SetInjection.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 io.github.cjstehno.testthings.rando.Randomizer;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.val;
import static java.util.Locale.ROOT;
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;
/**
* Injection instance used to inject a value into a field, optionally using an existing setter method.
*/
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
class SetInjection implements Injection {
private final String name;
private final Object value;
private final boolean preferSetter;
/**
* 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 {
injectValue(
instance,
value instanceof Randomizer<?> rando ? rando.one() : value
);
}
private void injectValue(final Object instance, final Object injectedValue) throws ReflectiveOperationException {
if (preferSetter) {
// try to use the setter method (if exists)
val methodName = "set" + name.substring(0, 1).toUpperCase(ROOT) + name.substring(1);
val methods = findMethods(
instance.getClass(),
m -> m.getName().equals(methodName) && m.getParameterCount() == 1 && isNotStatic(m),
TOP_DOWN
);
if (!methods.isEmpty()) {
methods.get(0).setAccessible(true);
methods.get(0).invoke(instance, injectedValue);
return;
}
}
// try to use the field if it exists
findFields(instance.getClass(), f -> f.getName().equals(name) && isNotStatic(f), TOP_DOWN).stream()
.peek(f -> f.setAccessible(true))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("Unable to inject value for '" + name + "'"))
.set(instance, injectedValue);
}
}