ClassInstance.java
/*
* SPDX-License-Identifier: Apache-2.0
* See LICENSE file for details.
*
* Copyright 2012-2026 hazendaz
*
* Portions of initial baseline code (getter/setter test) by Rob Dawson (CodeBox)
*/
package com.codebox.instance;
import com.codebox.bean.ValueBuilder;
import com.codebox.enums.LoadData;
import com.codebox.enums.LoadType;
import com.codebox.util.LombokBuilderUtil;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.junit.jupiter.api.Assertions;
/**
* The Class Instance.
*
* @param <T>
* the element type
*/
public class ClassInstance<T> {
/**
* New instance. This will get the first available constructor to run the test on. This allows for instances where
* there is intentionally not a no-arg constructor.
*
* @param clazz
* the class
*
* @return the t
*/
@SuppressWarnings("unchecked")
public final T newInstance(final Class<T> clazz) {
// If class is null, return null
if (clazz == null) {
return null;
}
// Check if class has Lombok @Builder pattern (static builder() method)
Method builderMethod = LombokBuilderUtil.getLombokBuilderMethod(clazz);
if (builderMethod != null) {
try {
return this.createInstanceWithBuilder(clazz, builderMethod);
} catch (final IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
Assertions.fail(String.format(
"An exception was thrown while creating instance using Lombok @Builder for class '%s': '%s'",
clazz.getName(), e.toString()));
}
}
// Try no-arg constructor first
for (final Constructor<?> constructor : clazz.getConstructors()) {
// Skip deprecated constructors
if (constructor.isAnnotationPresent(Deprecated.class)) {
continue;
}
// Find available no-arg constructor and return it
if (constructor.getParameterCount() == 0) {
try {
return (T) constructor.newInstance();
} catch (final InstantiationException | IllegalAccessException | InvocationTargetException e) {
Assertions.fail(String.format(
"An exception was thrown while testing class no-arg constructor (new instance) '%s': '%s'",
constructor.getName(), e.toString()));
}
}
}
// Try any constructor
for (final Constructor<?> constructor : clazz.getConstructors()) {
// Skip deprecated constructors
if (constructor.isAnnotationPresent(Deprecated.class)) {
continue;
}
final Class<?>[] types = constructor.getParameterTypes();
final Object[] values = new Object[constructor.getParameterTypes().length];
// Load Data
for (int i = 0; i < values.length; i++) {
values[i] = this.buildValue(types[i], LoadType.STANDARD_DATA);
}
try {
return (T) constructor.newInstance(values);
} catch (final InstantiationException | IllegalAccessException | InvocationTargetException e) {
Assertions.fail(String.format(
"An exception was thrown while testing the class (new instance) '%s' with '%s': '%s'",
constructor.getName(), Arrays.toString(values), e.toString()));
}
}
return null;
}
/**
* Create an instance using Lombok's builder pattern.
*
* @param clazz
* the class to instantiate
* @param builderMethod
* the builder method
* @return the instance created via builder
* @throws IllegalAccessException
* the illegal access exception
* @throws IllegalArgumentException
* the illegal argument exception
* @throws InvocationTargetException
* the invocation target exception
* @throws NoSuchMethodException
* the no such method exception
* @throws SecurityException
* the security exception
*/
@SuppressWarnings("unchecked")
private T createInstanceWithBuilder(final Class<T> clazz, final Method builderMethod) throws IllegalAccessException,
IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
// Invoke the builder() method to get a builder instance
final T builder = (T) builderMethod.invoke(null);
// Find the build() method on the builder
Method buildMethod = builder.getClass().getMethod("build");
// Invoke build() to create the instance
return (T) buildMethod.invoke(builder);
}
/**
* Builds the value.
*
* @param <R>
* the generic type
* @param returnType
* the return type
* @param loadType
* the load type
*
* @return the object
*/
public <R> Object buildValue(final Class<R> returnType, final LoadType loadType) {
final ValueBuilder valueBuilder = new ValueBuilder();
valueBuilder.setLoadData(LoadData.ON);
return valueBuilder.buildValue(returnType, loadType);
}
}