Skip to content

Commit

Permalink
Allowed converter overrides from outside
Browse files Browse the repository at this point in the history
  • Loading branch information
kpartlow committed Feb 13, 2024
1 parent 19d8bed commit 3ce03dd
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 43 deletions.
27 changes: 27 additions & 0 deletions src/main/java/com/cedarsoftware/util/ClassUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class ClassUtilities
{
private static final Set<Class<?>> prims = new HashSet<>();

private static final Map<Class<?>, Class<?>> primitiveToWrapper = new HashMap<>(20, .8f);
private static final Map<String, Class<?>> nameToClass = new HashMap<>();

static
Expand All @@ -65,6 +66,16 @@ public class ClassUtilities
nameToClass.put("date", Date.class);
nameToClass.put("class", Class.class);

primitiveToWrapper.put(int.class, Integer.class);
primitiveToWrapper.put(long.class, Long.class);
primitiveToWrapper.put(double.class, Double.class);
primitiveToWrapper.put(float.class, Float.class);
primitiveToWrapper.put(boolean.class, Boolean.class);
primitiveToWrapper.put(char.class, Character.class);
primitiveToWrapper.put(byte.class, Byte.class);
primitiveToWrapper.put(short.class, Short.class);
primitiveToWrapper.put(void.class, Void.class);

}

/**
Expand Down Expand Up @@ -307,4 +318,20 @@ public static boolean areAllConstructorsPrivate(Class<?> c) {

return true;
}

public static Class<?> toPrimitiveWrapperClass(Class<?> primitiveClass) {
if (!primitiveClass.isPrimitive()) {
return primitiveClass;
}

Class<?> c = primitiveToWrapper.get(primitiveClass);

if (c == null) {
throw new IllegalArgumentException("Passed in class: " + primitiveClass + " is not a primitive class");
}

return c;
}


}
48 changes: 11 additions & 37 deletions src/main/java/com/cedarsoftware/util/convert/Converter.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.util.AbstractMap;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
Expand All @@ -35,6 +34,8 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import com.cedarsoftware.util.ClassUtilities;

/**
* Instance conversion utility. Convert from primitive to other primitives, plus support for Number, Date,
* TimeStamp, SQL Date, LocalDate, LocalDateTime, ZonedDateTime, Calendar, Big*, Atomic*, Class, UUID,
Expand Down Expand Up @@ -78,7 +79,6 @@ public final class Converter {
private final ConverterOptions options;

private static final Map<Class<?>, Set<ClassLevel>> cacheParentTypes = new ConcurrentHashMap<>();
private static final Map<Class<?>, Class<?>> primitiveToWrapper = new HashMap<>(20, .8f);
private static final Map<Map.Entry<Class<?>, Class<?>>, Convert<?>> CONVERSION_DB = new ConcurrentHashMap<>(500, .8f);

// Create a Map.Entry (pair) of source class to target class.
Expand All @@ -87,26 +87,13 @@ static Map.Entry<Class<?>, Class<?>> pair(Class<?> source, Class<?> target) {
}

static {
buildPrimitiveWrappers();
buildFactoryConversions();
}

public ConverterOptions getOptions() {
return options;
}

private static void buildPrimitiveWrappers() {
primitiveToWrapper.put(int.class, Integer.class);
primitiveToWrapper.put(long.class, Long.class);
primitiveToWrapper.put(double.class, Double.class);
primitiveToWrapper.put(float.class, Float.class);
primitiveToWrapper.put(boolean.class, Boolean.class);
primitiveToWrapper.put(char.class, Character.class);
primitiveToWrapper.put(byte.class, Byte.class);
primitiveToWrapper.put(short.class, Short.class);
primitiveToWrapper.put(void.class, Void.class);
}

private static void buildFactoryConversions() {
// toByte
CONVERSION_DB.put(pair(Void.class, byte.class), NumberConversions::toByteZero);
Expand Down Expand Up @@ -859,6 +846,7 @@ private static void buildFactoryConversions() {
public Converter(ConverterOptions options) {
this.options = options;
this.factory = new ConcurrentHashMap<>(CONVERSION_DB);
this.factory.putAll(this.options.getConverterOverrides());
}

/**
Expand Down Expand Up @@ -901,7 +889,7 @@ public <T> T convert(Object from, Class<T> toType) {
// Promote primitive to primitive wrapper, so we don't have to define so many duplicates in the factory map.
sourceType = from.getClass();
if (toType.isPrimitive()) {
toType = (Class<T>) toPrimitiveWrapperClass(toType);
toType = (Class<T>) ClassUtilities.toPrimitiveWrapperClass(toType);
}
}

Expand Down Expand Up @@ -1038,8 +1026,8 @@ static private String name(Object from) {
* @return boolean true if the Converter converts from the source type to the destination type, false otherwise.
*/
boolean isDirectConversionSupportedFor(Class<?> source, Class<?> target) {
source = toPrimitiveWrapperClass(source);
target = toPrimitiveWrapperClass(target);
source = ClassUtilities.toPrimitiveWrapperClass(source);
target = ClassUtilities.toPrimitiveWrapperClass(target);
Convert<?> method = factory.get(pair(source, target));
return method != null && method != UNSUPPORTED;
}
Expand All @@ -1052,8 +1040,8 @@ boolean isDirectConversionSupportedFor(Class<?> source, Class<?> target) {
* @return boolean true if the Converter converts from the source type to the destination type, false otherwise.
*/
public boolean isConversionSupportedFor(Class<?> source, Class<?> target) {
source = toPrimitiveWrapperClass(source);
target = toPrimitiveWrapperClass(target);
source = ClassUtilities.toPrimitiveWrapperClass(source);
target = ClassUtilities.toPrimitiveWrapperClass(target);
Convert<?> method = factory.get(pair(source, target));
if (method != null && method != UNSUPPORTED) {
return true;
Expand Down Expand Up @@ -1104,28 +1092,14 @@ public Map<String, Set<String>> getSupportedConversions() {
* @return prior conversion function if one existed.
*/
public Convert<?> addConversion(Class<?> source, Class<?> target, Convert<?> conversionFunction) {
source = toPrimitiveWrapperClass(source);
target = toPrimitiveWrapperClass(target);
source = ClassUtilities.toPrimitiveWrapperClass(source);
target = ClassUtilities.toPrimitiveWrapperClass(target);
return factory.put(pair(source, target), conversionFunction);
}

/**
* Given a primitive class, return the Wrapper class equivalent.
*/
static Class<?> toPrimitiveWrapperClass(Class<?> primitiveClass) {
if (!primitiveClass.isPrimitive()) {
return primitiveClass;
}

Class<?> c = primitiveToWrapper.get(primitiveClass);

if (c == null) {
throw new IllegalArgumentException("Passed in class: " + primitiveClass + " is not a primitive class");
}

return c;
}

private static <T> T identity(T from, Converter converter) {
return from;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

/**
Expand Down Expand Up @@ -68,4 +70,10 @@ public interface ConverterOptions {
* @return the Character representing false
*/
default Character falseChar() { return CommonValues.CHARACTER_ZERO; }

/**
* Overrides for converter conversions..
* @return The Map of overrides.
*/
default Map<Map.Entry<Class<?>, Class<?>>, Convert<?>> getConverterOverrides() { return new HashMap<>(); }
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package com.cedarsoftware.util.convert;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

Expand All @@ -28,13 +24,19 @@ public class DefaultConverterOptions implements ConverterOptions {

private final Map<String, Object> customOptions;

private final Map<Map.Entry<Class<?>, Class<?>>, Convert<?>> converterOverrides;

public DefaultConverterOptions() {
this.customOptions = new ConcurrentHashMap<>();
this.converterOverrides = new ConcurrentHashMap<>();
}

@SuppressWarnings("unchecked")
@Override
public <T> T getCustomOption(String name) {
return (T) this.customOptions.get(name);
}

@Override
public Map<Map.Entry<Class<?>, Class<?>>, Convert<?>> getConverterOverrides() { return this.converterOverrides; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import com.cedarsoftware.util.ClassUtilities;

import static com.cedarsoftware.util.MapUtilities.mapOf;
import static com.cedarsoftware.util.convert.Converter.getShortName;
import static com.cedarsoftware.util.convert.Converter.pair;
Expand Down Expand Up @@ -1676,9 +1678,9 @@ void testConvert(String shortNameSource, String shortNameTarget, Object source,
if (source == null) {
assertEquals(sourceClass, Void.class, "On the source-side of test input, null can only appear in the Void.class data");
} else {
assertTrue(Converter.toPrimitiveWrapperClass(sourceClass).isInstance(source), "source type mismatch");
assertTrue(ClassUtilities.toPrimitiveWrapperClass(sourceClass).isInstance(source), "source type mismatch");
}
assertTrue(target == null || target instanceof Throwable || Converter.toPrimitiveWrapperClass(targetClass).isInstance(target), "target type mismatch");
assertTrue(target == null || target instanceof Throwable || ClassUtilities.toPrimitiveWrapperClass(targetClass).isInstance(target), "target type mismatch");

// if the source/target are the same Class, then ensure identity lambda is used.
if (sourceClass.equals(targetClass)) {
Expand Down

0 comments on commit 3ce03dd

Please sign in to comment.