Last active
August 29, 2015 14:19
-
-
Save ledoyen/069fa3ee759f6d600433 to your computer and use it in GitHub Desktop.
[JAVA] Generate default values
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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