Skip to content

Commit

Permalink
Initial thoughts on how to allow type conversion to be extensible
Browse files Browse the repository at this point in the history
  • Loading branch information
clayreimann committed Mar 10, 2017
1 parent db25820 commit 60fc665
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,16 @@

import com.yahoo.elide.core.exceptions.InvalidAttributeException;
import com.yahoo.elide.core.exceptions.InvalidValueException;
import com.yahoo.elide.utils.coerce.converters.EpochToDateConverter;
import com.yahoo.elide.utils.coerce.converters.FromMapConverter;
import com.yahoo.elide.utils.coerce.converters.ToEnumConverter;
import com.yahoo.elide.utils.coerce.converters.ElideConverter;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.lang3.ClassUtils;

import java.util.Date;
import java.util.Map;

/**
* Class for coercing a value to a target class.
*/
public class CoerceUtil {

private static final ToEnumConverter TO_ENUM_CONVERTER = new ToEnumConverter();
private static final FromMapConverter FROM_MAP_CONVERTER = new FromMapConverter();
private static final EpochToDateConverter EPOCH_TO_DATE_CONVERTER = new EpochToDateConverter();

//static block for setup and registering new converters
static {
setup();
Expand Down Expand Up @@ -57,30 +45,6 @@ public static <T> T coerce(Object value, Class<T> cls) {
* Perform CoerceUtil setup.
*/
private static void setup() {
BeanUtilsBean.setInstance(new BeanUtilsBean(new ConvertUtilsBean() {
{
// https://github.com/yahoo/elide/issues/260
// enable throwing exceptions when conversion fails
register(true, false, 0);
}

@Override
/*
* Overriding lookup to execute enum converter if target is enum
* or map convert if source is map
*/
public Converter lookup(Class<?> sourceType, Class<?> targetType) {
if (targetType.isEnum()) {
return TO_ENUM_CONVERTER;
} else if (Map.class.isAssignableFrom(sourceType)) {
return FROM_MAP_CONVERTER;
} else if ((String.class.isAssignableFrom(sourceType) || Number.class.isAssignableFrom(sourceType))
&& ClassUtils.isAssignable(targetType, Date.class)) {
return EPOCH_TO_DATE_CONVERTER;
} else {
return super.lookup(sourceType, targetType);
}
}
}));
BeanUtilsBean.setInstance(new BeanUtilsBean(new ElideConverter()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2017, Yahoo Inc.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/

package com.yahoo.elide.utils.coerce.converters;

import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.lang3.ClassUtils;

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiFunction;

/**
* A class that knows how to convert thing to other (different) things.
*/
public class ElideConverter extends ConvertUtilsBean {
private static final ToEnumConverter TO_ENUM_CONVERTER = new ToEnumConverter();
private static final FromMapConverter FROM_MAP_CONVERTER = new FromMapConverter();
private static final EpochToDateConverter EPOCH_TO_DATE_CONVERTER = new EpochToDateConverter();

private LinkedHashMap<BiFunction<Class<?>, Class<?>, Boolean>, Converter> converters;

/**
* Create a new ElideConverter with a set of converters.
*
* @param converters extra converters to consider before the default converters fire
*/
public ElideConverter(LinkedHashMap<BiFunction<Class<?>, Class<?>, Boolean>, Converter> converters) {
// yahoo/elide#260 - enable throwing exceptions when conversion fails
register(true, false, 0);
this.converters = converters;
}

/**
* Create a new ElideConverter with the default set of converters.
*/
public ElideConverter() {
this(defaultConverters());
}

@Override
/*
* Overriding lookup to execute enum converter if target is enum
* or map convert if source is map
*/
public Converter lookup(Class<?> sourceType, Class<?> targetType) {
for (Map.Entry<BiFunction<Class<?>, Class<?>, Boolean>, Converter> entry : converters.entrySet()) {
if (entry.getKey().apply(sourceType, targetType)) {
return entry.getValue();
}
}
return super.lookup(sourceType, targetType);
}

public static LinkedHashMap<BiFunction<Class<?>, Class<?>, Boolean>, Converter> defaultConverters() {
LinkedHashMap<BiFunction<Class<?>, Class<?>, Boolean>, Converter> converters = new LinkedHashMap<>();
converters.put((source, target) -> target.isEnum(), TO_ENUM_CONVERTER);
converters.put((source, target) -> Map.class.isAssignableFrom(source), FROM_MAP_CONVERTER);
converters.put(
(source, target) -> (String.class.isAssignableFrom(source) || Number.class.isAssignableFrom(source))
&& ClassUtils.isAssignable(target, Date.class),
EPOCH_TO_DATE_CONVERTER);
return converters;
}
}

0 comments on commit 60fc665

Please sign in to comment.