Skip to content

Instantly share code, notes, and snippets.

@ledoyen
Last active August 29, 2015 14:19
Show Gist options
  • Save ledoyen/069fa3ee759f6d600433 to your computer and use it in GitHub Desktop.
Save ledoyen/069fa3ee759f6d600433 to your computer and use it in GitHub Desktop.
[JAVA] Generate default values
package tools;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Optional;
import org.mockito.internal.util.Primitives;
import com.google.common.base.Preconditions;
import tools.Tuple;
/**
* Utility computing default values for given types.
*/
public final class DefaultValues {
/** utility class : private constructor. */
private DefaultValues() {
}
/**
* @param types array of types to compute default values for
* @param nullPolicy policy for default value generation
* @return an array (same same as given types array) containing default values
*/
public static Object[] defaultValues(final Class<?>[] types, final NullPolicy nullPolicy) {
Object[] result = new Object[types.length];
for (int i = 0; i < types.length; i++) {
result[i] = defaultValue(types[i], nullPolicy);
}
return result;
}
/**
* @param <T> type of the value to compute
* @param type to compute default value for
* @param nullPolicy policy for default value generation.
* @return computed default value
*/
public static <T> T defaultValue(final Class<T> type, final NullPolicy nullPolicy) {
final T result;
if (type.isPrimitive()) {
result = Primitives.defaultValueForPrimitiveOrWrapper(type);
} else if (NullPolicy.NULL == nullPolicy) {
result = null;
} else if (Primitives.isPrimitiveOrWrapper(type)) {
result = Primitives.defaultValueForPrimitiveOrWrapper(type);
} else {
result = defaultValueForNonPrimitive(type, nullPolicy);
}
return result;
}
/**
* @param <T> type of the value to compute
* @param type to compute default value for
* @param nullPolicy policy for default value generation.
* @return computed default value
*/
private static <T> T defaultValueForNonPrimitive(final Class<T> type, final NullPolicy nullPolicy) {
Preconditions.checkArgument(!Primitives.isPrimitiveOrWrapper(type), "type must not be a primitive or primitive-wrapper one");
final T result;
Optional<Constructor<T>> constructor = findMinimalConstructor(type);
if (constructor.isPresent()) {
try {
result = constructor.get().newInstance(defaultValues(constructor.get().getParameterTypes(), nullPolicy));
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
return null;
}
} else {
result = null;
}
return result;
}
/**
* Find the minimal constructor, meaning the one with minimal effort to use.
* @param <T> the type the constructors are about
* @param type the class for which to find the minimal constructor
* @return the minimal {@link Constructor} from the given list
*/
private static <T> Optional<Constructor<T>> findMinimalConstructor(final Class<T> type) {
@SuppressWarnings("unchecked")
Constructor<T>[] constructors = (Constructor<T>[]) type.getConstructors();
return Arrays.asList(constructors).stream().map(c -> Tuple.of(countPrimAndNonPrim(c.getParameterTypes()), c)).sorted((o1, o2) -> {
// Same number of non-prim
if (o1.value1().value2().equals(o2.value1().value2())) {
// select the one with the lower number of prim
return o1.value1().value1().compareTo(o2.value1().value1());
} else {
// select the one with the lower number of non-prim
return o1.value1().value2().compareTo(o2.value1().value2());
}
}).map(tt -> tt.value2()).findFirst();
}
/**
* @param types to count for primitives (and wrappers) and non-primitives
* @return a tuple with the count of primitive types (and wrappers) in first slot, the count of non-primitive ones in the secopnd slot
*/
private static Tuple<Integer, Integer> countPrimAndNonPrim(final Class<?>[] types) {
int countPrim = 0;
int countNonPrim = 0;
for (Class<?> type : types) {
if (Primitives.isPrimitiveOrWrapper(type)) {
countPrim++;
} else {
countNonPrim++;
}
}
return Tuple.of(countPrim, countNonPrim);
}
/**
* Policy for default value generation.
*/
public static enum NullPolicy {
/** Use null value wherever it is possible (every types except primitive ones). */
NULL,
/** Use null nowhere, except where it is impossible (types with missing constructor). */
NOT_NULL;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment