TypeSetInjection.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.RequiredArgsConstructor;
import lombok.val;
import java.util.HashSet;
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;
/**
* Provides injection of values based on the field/property type rather than the name.
*/
@RequiredArgsConstructor(access = PACKAGE)
public class TypeSetInjection implements Injection {
private final Class<?> type;
private final Object value;
private final boolean preferSetter;
/**
* Injects the value into all properties/fields of the configured type.
*
* @param instance the instance where things should be injected.
* @throws ReflectiveOperationException if there is a problem
*/
@Override public void injectInto(final Object instance) throws ReflectiveOperationException {
val injectedNames = new HashSet<String>();
// inject via setter if requested
if (preferSetter) {
for (val setter : findMethods(
instance.getClass(),
m -> isNotStatic(m) && m.getParameterCount() == 1 && type.isAssignableFrom(m.getParameterTypes()[0]) && m.getName().startsWith("set"),
TOP_DOWN
)) {
setter.setAccessible(true);
setter.invoke(instance, injectedValue());
// make sure we don't inject twice
injectedNames.add(setter.getName().substring(3).toLowerCase(ROOT));
}
}
// inject via field otherwise
for (val field : findFields(
instance.getClass(),
f -> isNotStatic(f) && type.isAssignableFrom(f.getType()),
TOP_DOWN
)) {
val name = field.getName().toLowerCase(ROOT);
if (!injectedNames.contains(name)) {
field.setAccessible(true);
field.set(instance, injectedValue());
}
}
}
private Object injectedValue() {
return value instanceof Randomizer<?> rando ? rando.one() : value;
}
}