Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Single dimension converters #23

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.smallrye.converters.api;

public interface ConvertedType<S> {
S getRaw();

<T> T getAs(Class<T> klass);
}
5 changes: 2 additions & 3 deletions api/src/main/java/io/smallrye/converters/api/Converter.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.smallrye.converters.api;

import java.io.Serializable;

public interface Converter<T> extends Serializable {
@FunctionalInterface
public interface Converter<T> {
T convert(String value);
}
13 changes: 7 additions & 6 deletions api/src/main/java/io/smallrye/converters/api/Converters.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package io.smallrye.converters.api;

import java.io.Serializable;
import java.util.Collection;
import java.util.Optional;
import java.util.function.IntFunction;

public interface Converters extends Serializable {
public interface Converters {
<T> Converter<T> getConverter(Class<T> asType);

<T> Converter<Optional<T>> getOptionalConverter(Class<T> asType);

<T> T convertValue(String value, Class<T> asType);
<T> T convert(String value, Class<T> asType);

<T> T convertValue(String value, Converter<T> converter);
<T> T convert(String value, Converter<T> converter);

<T, C extends Collection<T>> C convertValues(String value, Class<T> asType, IntFunction<C> collectionFactory);
<T, C extends Collection<T>> C convert(String value, Class<T> asType, IntFunction<C> collectionFactory);

<T, C extends Collection<T>> C convertValues(String value, Converter<T> converter, IntFunction<C> collectionFactory);
<T, C extends Collection<T>> C convert(String value, Converter<T> converter, IntFunction<C> collectionFactory);

<S, T> ConvertedType<T> from(S value);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.smallrye.converters.api;

public final class ConvertersProvider {
private ConvertersProvider() {
throw new UnsupportedOperationException();
}

public static Converters getConverters() {
return ConvertersProviderResolver.instance().getConverters();
}

public static Converters getConverters(final ClassLoader classLoader) {
return ConvertersProviderResolver.instance().getConverters(classLoader);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.smallrye.converters.api;

import java.util.Iterator;
import java.util.ServiceLoader;

public abstract class ConvertersProviderResolver {
private static volatile ConvertersProviderResolver instance = null;

public abstract Converters getConverters();

public abstract Converters getConverters(ClassLoader classLoader);

protected ConvertersProviderResolver() {

}

public static ConvertersProviderResolver instance() {
if (instance == null) {
synchronized (ConvertersProviderResolver.class) {
if (instance != null) {
return instance;
}
instance = loadSpi(ConvertersProviderResolver.class.getClassLoader());
}
}

return instance;
}

private static ConvertersProviderResolver loadSpi(ClassLoader cl) {
ServiceLoader<ConvertersProviderResolver> sl = ServiceLoader.load(
ConvertersProviderResolver.class, cl);
final Iterator<ConvertersProviderResolver> iterator = sl.iterator();
if (iterator.hasNext()) {
return iterator.next();
}
throw new IllegalStateException(
"No ConvertersProviderResolver implementation found!");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.smallrye.converters.api;

@FunctionalInterface
public interface InputConverter<S, T> {
T convert(S value);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.smallrye.converters.api;

@FunctionalInterface
public interface OutputConverter<S, T> {
T convert(ConvertedType<S> value);
}
20 changes: 20 additions & 0 deletions implementation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,26 @@
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
</dependency>

<dependency>
<groupId>jakarta.json.bind</groupId>
<artifactId>jakarta.json.bind-api</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<version>1.0.6</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.26</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package io.smallrye.converters;

import java.lang.reflect.Type;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import io.smallrye.converters.api.ConvertedType;
import io.smallrye.converters.api.Converter;
import io.smallrye.converters.api.Converters;
import io.smallrye.converters.api.ConvertersProvider;
import io.smallrye.converters.api.OutputConverter;

public class ConvertedTypeImpl<S> implements ConvertedType<S> {
private final S value;
private final Map<Type, OutputConverter<S, ?>> converters;

public ConvertedTypeImpl(final S value, final Map<Type, OutputConverter<S, ?>> converters) {
assert value != null;

this.value = value;
this.converters = converters;
}

public Object getValue() {
return value;
}

public String getAsString() {
return value.toString();
}

public Number getAsNumber() {
return getAs(Double.class);
}

public Boolean getAsBoolean() {
return getAs(Boolean.class);
}

@Override
public S getRaw() {
return value;
}

@Override
@SuppressWarnings("unchecked")
public <T> T getAs(Class<T> klass) {
if (klass.isInstance(value)) {
return (T) value;
}

OutputConverter<S, T> converter = (OutputConverter<S, T>) converters.get(klass);
if (converter == null) {
throw ConverterMessages.msg.noRegisteredConverter(klass);
}

return converter.convert((this));
}

@SuppressWarnings("unchecked")
public <T> List<T> getAsList(Class<T> klass) {
if (List.class.isAssignableFrom(value.getClass())) {
return (List<T>) ((List) value).stream()
.map(o -> ConvertedTypeImpl.of(o).getAs(klass))
.collect(Collectors.toList());
}

if (value instanceof String) {
return (List<T>) ConvertersUtils.newCollectionConverter(
(Converter<Object>) value -> ConvertedTypeImpl.of(value).getAs(klass), ArrayList::new)
.convert((String) value);
}

throw new IllegalStateException();
}

public <K, V> Map<K, V> getAsMap(Class<K> key, Class<V> value) {
if (Map.class.isAssignableFrom(this.value.getClass())) {
return ((Set<Map.Entry>) ((Map) this.value).entrySet())
.stream()
.map(entry -> new AbstractMap.SimpleEntry<>(ConvertedTypeImpl.of(entry.getKey()).getAs(key),
ConvertedTypeImpl.of(entry.getValue()).getAs(value)))
.collect(Collectors.toMap(entry -> (K) entry.getKey(),
entry -> (V) entry.getValue()));
}

throw new IllegalStateException();
}

static <S> ConvertedTypeImpl<S> of(final S rawValue) {
return of(rawValue, ConvertersProvider.getConverters());
}

static <S> ConvertedTypeImpl<S> of(final S rawValue, final Converters converters) {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
*
* @author <a href="http://jmesnil.net/">Jeff Mesnil</a> (c) 2017 Red Hat inc.
*/
public final class Converters {
private Converters() {
public final class ConvertersUtils {
private ConvertersUtils() {
}

static final Converter<String> STRING_CONVERTER = BuiltInConverter.of(0, newEmptyValueConverter(value -> value));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,36 @@

import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.IntFunction;

import io.smallrye.converters.api.ConvertedType;
import io.smallrye.converters.api.Converter;
import io.smallrye.converters.api.InputConverter;
import io.smallrye.converters.api.OutputConverter;

public class SmallRyeConverters implements io.smallrye.converters.api.Converters {
private final Map<Type, Converter<?>> converters;
private final Map<Type, Converter<Optional<?>>> optionalConverters = new ConcurrentHashMap<>();

private final Map<Type, InputConverter<?, ?>> inputConverters = new HashMap<>();
private final Map<Type, OutputConverter<?, ?>> outputConverters = new HashMap<>();

SmallRyeConverters() {
converters = new ConcurrentHashMap<>(Converters.ALL_CONVERTERS);
converters = new ConcurrentHashMap<>(ConvertersUtils.ALL_CONVERTERS);
}

SmallRyeConverters(final SmallRyeConvertersBuilder smallRyeConvertersBuilder) {
SmallRyeConverters(final SmallRyeConvertersBuilder builder) {
this();
smallRyeConvertersBuilder.getConverters()
builder.getConverters()
.forEach((type, converter) -> this.converters.put(type, converter.getConverter()));

// TODO - add Priority
inputConverters.putAll(builder.getInputConverters());
outputConverters.putAll(builder.getOutputConverters());
}

@SuppressWarnings("unchecked")
Expand All @@ -31,11 +42,11 @@ public <T> Converter<T> getConverter(Class<T> asType) {
return (Converter<T>) exactConverter;
}
if (asType.isPrimitive()) {
return (Converter<T>) getConverter(Converters.wrapPrimitiveType(asType));
return (Converter<T>) getConverter(ConvertersUtils.wrapPrimitiveType(asType));
}
if (asType.isArray()) {
final Converter<?> conv = getConverter(asType.getComponentType());
return conv == null ? null : Converters.newArrayConverter(conv, asType);
return conv == null ? null : ConvertersUtils.newArrayConverter(conv, asType);
}
return (Converter<T>) converters.computeIfAbsent(asType, clazz -> ImplicitConverters.getConverter((Class<?>) clazz));
}
Expand All @@ -44,28 +55,58 @@ public <T> Converter<T> getConverter(Class<T> asType) {
@Override
public <T> Converter<Optional<T>> getOptionalConverter(Class<T> asType) {
return optionalConverters.computeIfAbsent(asType,
clazz -> Converters.newOptionalConverter(getConverter((Class) clazz)));
clazz -> ConvertersUtils.newOptionalConverter(getConverter((Class) clazz)));
}

@Override
public <T> T convertValue(final String value, final Class<T> asType) {
public <T> T convert(final String value, final Class<T> asType) {
return getConverter(asType).convert(value);
}

@Override
public <T> T convertValue(final String value, final Converter<T> converter) {
public <T> T convert(final String value, final Converter<T> converter) {
return converter.convert(value);
}

@Override
public <T, C extends Collection<T>> C convertValues(final String value, final Class<T> asType,
public <T, C extends Collection<T>> C convert(final String value, final Class<T> asType,
final IntFunction<C> collectionFactory) {
return convertValues(value, getConverter(asType), collectionFactory);
return convert(value, getConverter(asType), collectionFactory);
}

@Override
public <T, C extends Collection<T>> C convertValues(final String value, final Converter<T> converter,
public <T, C extends Collection<T>> C convert(final String value, final Converter<T> converter,
final IntFunction<C> collectionFactory) {
return convertValue(value, Converters.newCollectionConverter(converter, collectionFactory));
return convert(value, ConvertersUtils.newCollectionConverter(converter, collectionFactory));
}

@Override
@SuppressWarnings("unchecked")
public <S, T> ConvertedType<T> from(final S value) {
InputConverter<S, T> inputConverter = (InputConverter<S, T>) getInputConverter(value.getClass());
T input = inputConverter.convert(value);
Map<Type, ? extends OutputConverter<?, ?>> outputConverters = getOutputConverters(value.getClass());
return new ConvertedTypeImpl<T>(input, (Map<Type, OutputConverter<T, ?>>) outputConverters);
}

@SuppressWarnings("unchecked")
private <S, T> InputConverter<S, T> getInputConverter(final Class<S> asType) {
InputConverter<S, ?> exactConverter = (InputConverter<S, ?>) inputConverters.get(asType);
if (exactConverter != null) {
return (InputConverter<S, T>) exactConverter;
}

// passthrough
return value -> (T) value;
}

@SuppressWarnings("unchecked")
private <S> Map<Type, OutputConverter<S, ?>> getOutputConverters(final Class<S> asType) {
Map<Type, OutputConverter<S, ?>> converters = new HashMap<>();
for (Map.Entry<Type, OutputConverter<?, ?>> entry : outputConverters.entrySet()) {
// TODO - Filter based on S,T of OutputConverter
converters.put(entry.getKey(), (OutputConverter<S, ?>) entry.getValue());
}
return converters;
}
}
Loading