Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- public interface Source {
- @Nullable
- Object getNullable(String key);
- @NonNull
- default Object get(String key) {
- final var result = getNullable(key);
- if (result == null) {
- throw new TypeException(key, ClassType.of(Object.class));
- }
- return result;
- }
- }
- public interface Type<T> {
- @NonNull
- T convert(@Nullable Object value, @NonNull Source source);
- static Type<?> of(java.lang.reflect.Type nativeType) {
- if (nativeType instanceof Class) {
- return of((Class<?>) nativeType);
- }
- final var p = (ParameterizedType) nativeType;
- final var r = p.getRawType();
- final var a = p.getActualTypeArguments();
- if (r == Set.class) {
- return new SetType<>(of(a[0]));
- }
- if (r == List.class || r == Collection.class) {
- return new ListType<>(of(a[0]));
- }
- if (r == Map.class) {
- return new MapType<>(of(a[0]), of(b[0]));
- }
- throw new IllegalArgumentException(nativeType.getTypeName());
- }
- static <T> Type<T> of(Class<T> clazz) {
- return ClassType.of(clazz);
- }
- }
- public class AbstractType<T> implements Type<T> {
- @NonNull
- public T convert(@Nullable Object value, @NonNull Source source) {
- return Objects.requireNonNull(convert0(value, source));
- }
- protected abstract boolean isInstance(@Nullable Object value);
- protected abstract Optional<Object> doConversion(@Nullable Object value, @NonNull Source source);
- @SuppressWarnings("unchecked")
- private T convert0(Object value, Source source) {
- if (isInstance(value)) {
- return (T) value;
- }
- return (T) doConversion(value, source)
- .or(() -> lookup(value, source)
- .orElseThrow(() -> new TypeError(value, this));
- }
- private Optional<Object> lookup(Object value, Source source) {
- if (value instanceof String) {
- final var base = source.get((String) value);
- final var result = convert0(base, source);
- return Optional.ofNullable(result);
- }
- return Optional.empty();
- }
- }
- public class ClassType<T> extends AbstractType<T> {
- private static final Map<Class<?>, ClassType<?>> CACHE = new ConcurrentHashMap<>(Map.of
- Boolean.class, BooleanType.BOOLEAN,
- Integer.class, NumberType.INTEGER,
- Double.class, NumberType.REAL,
- String.class, StringType.STRING,
- Object.class, ObjectType.DEFINED);
- final Class<T> clazz;
- ClassType(Class<T> clazz) {
- this.clazz = Objects.requireNonNull(clazz);
- }
- @SuppressWarnings("unchecked")
- public static <T> ClassType<T> of(Class<T> clazz) {
- return (ClassType) CACHE.computeIfAbsent(clazz, c -> {
- if (c.isEnum()) {
- return new EnumClassType<>(c);
- }
- return new SimpleClassType<>(c);
- });
- }
- @Override
- public int hashCode() {
- return clazz.hashCode();
- }
- @Override
- public boolean equals(Object other) {
- if (other == this) return true;
- if (other == null || getClass() != other.getClass()) return false;
- return clazz == ((ClassType<?>) other).clazz;
- }
- @Override
- public String toString() {
- return clazz.getSimpleName();
- }
- public Class<T> unwrap() {
- return clazz;
- }
- @Override
- protected boolean isInstance(@Nullable Object value) {
- return clazz.isInstance(value);
- }
- }
- class SimpleClassType<T> extends ClassType<T> {
- SimpleClassType(Class<T> clazz) {
- super(clazz);
- }
- @Override
- protected Optional<Object> doConversion(@Nullable Object value, @NonNull Source source) {
- if (value == null) return doConversion(JSONObject.NULL, source);
- // TODO: factory methods
- return Optional.empty();
- }
- }
- class EnumClassType<T> extends ClassType<T> {
- private final Map<String, T> map = new CaseInsensitiveMap<>();
- private final Optional<Object> defaultValue;
- EnumClassType(Class<T> clazz) {
- super(clazz);
- for (final var t: clazz.getEnumConstants()) {
- map.put(t.name(), t);
- }
- defaultValue = Optional
- .ofNullable(clazz.getAnnotation(Default.class))
- .map(Default::value)
- .map(map::get);
- }
- @Override
- protected Optional<Object> doConversion(@Nullable Object value, @NonNull Source source) {
- if (value == null || value == JSONObject.NULL) return defaultValue;
- return Optional.ofNullable(map.get(String.valueOf(value));
- }
- }
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.TYPE)
- public @interface Default {
- String value();
- }
- public abstract class CollectionType<E, C extends Collection<E>> extends Type<C> {
- private final Type<E> elementType;
- public CollectionType(@NonNull Type<E> elementType) {
- this.elementType = Objects.requireNonNull(elementType);
- }
- @NonNull @Override
- public Set<E> convert(@Nullable Object value, @NonNull Source source) {
- final var result = getEmptyMutableInstance();
- if (value == null || value == JSONObject.NULL) {
- return result;
- }
- if (value instanceof Iterable) {
- for (final var i: (Iterable<?>) value) {
- result.add(elementType.convert(value, source));
- }
- } else {
- result.add(elementType.convert(value, source));
- }
- return result;
- }
- public Type<E> getElementType() {
- return elementType;
- }
- @Override
- public int hashCode() {
- return elementType.hashCode() * 7;
- }
- @Override
- public boolean equals(Object other) {
- if (other == this) return true;
- if (other == null || getClass() != other.getClass()) return false;
- final var setType = (SetType<?>) other;
- return elementType.equals(setType.elementType);
- }
- protected abstract C getEmptyMutableInstance();
- }
- public class SetType<E> extends SetType<E> {
- public SetType(Type<E> elementType) {
- super(elementType);
- }
- @Override
- public String toString() {
- return String.format("{%s}", elementType);
- }
- @Override
- protected Set<E> getEmptyMutableInstance() {
- return new HashSet<>();
- }
- }
- public class ListType<E> extends ListType<E> {
- public ListType(Type<E> elementType) {
- super(elementType);
- }
- @Override
- public String toString() {
- return String.format("[%s]", elementType);
- }
- @Override
- protected List<E> getEmptyMutableInstance() {
- return new ArrayList<>();
- }
- }
- public class MapType<K, V> extends Type<Map<K, V>> {
- private final Type<K> keyType;
- private final Type<V> valueType;
- public MapType(@NonNull Type<K> keyType, @NonNull Type<V> valueType) {
- this.keyType = Objects.requireNonNull(keyType);
- this.valueType = Objects.requireNonNull(valueType);
- }
- @NonNull @Override
- public Map<K, V> convert(@Nullable Object value, @NonNull Source source) {
- if (value instanceof JSONObject) {
- final var j = (JSONObject) value;
- return convert(j.keySet(), j::get, source);
- }
- if (value instanceof Map) {
- return convert((Map<?, ?>) value, source);
- }
- if (value == null || value == JSONObject.NULL) {
- return Map.of();
- }
- throw new TypeException(value, this);
- }
- public Type<K> getKeyType() {
- return keyType;
- }
- public Type<V> getValueType() {
- return valueType;
- }
- @Override
- public int hashCode() {
- return Objects.hash(keyType, valueType);
- }
- @Override
- public boolean equals(Object other) {
- if (other == this) return true;
- if (other == null || getClass() != other.getClass()) return false;
- final var mapType = (MapType<?, ?>) other;
- return keyType.equals(mapType.keyType)
- && valueType.equals(mapType.valueType);
- }
- @Override
- public String toString() {
- return String.format("{%s: %s}", keyType, valueType);
- }
- private <T> Map<K, V> convert(Map<T, ?> map, Source source) {
- return convert(map.keySet(), map::get, source);
- }
- private <T> Map<K, V> convert(Set<T> keySet, Function<T, ?> get, Source source) {
- final var result = new HashMap<K, V>(keySet.size());
- for (final var i: keySet) {
- final var k = keyType.convert(i, source);
- final var v = valueType.convert(get.apply(i), source);
- result.put(k, v);
- }
- return result;
- }
- }
- public class UnionType<T, A extends U, B extends U> extends Type<T> {
- private final Type<A> leftType;
- private final Type<B> rightType;
- public UnionType(Type<A> leftType, Type<B> rightType) {
- this.leftType = leftType;
- this.rightType = rightType;
- }
- @Override
- public T convert(@Nullable Object value, @NonNull Source source) {
- try {
- return leftType.convert(value, source);
- } catch (final TypeException ignored) {}
- try {
- return rightType.convert(value, source);
- } catch (final TypeException ignored) {
- throw new TypeException(value, this);
- }
- };
- public Type<A> getLeftType() {
- return leftType;
- }
- public Type<B> getRightType() {
- return rightType;
- }
- @Override
- public int hashCode() {
- return Objects.hash(a, b);
- }
- @Override
- public boolean equals(Object other) {
- if (other == this) return true;
- if (other == null || getClass() != other.getClass()) return false;
- final var unionType = (UnionType<?, ?, ?>) other;
- return a.equals(unionType.a)
- && b.equals(unionType.b);
- }
- @Override
- public String toString() {
- return String.format("%s|%s", a, b);
- }
- }
- public class ObjectType extends ClassType<Object> {
- public static final ObjectType DEFINED = new ObjectType();
- @NonNull @Override
- protected boolean isInstance(@Nullable Object value) {
- return value != null && value != JSONObject.NULL;
- }
- @NonNull @Override
- protected Optional<Object> doConversion(@Nullable Object value, @NonNull Source source) {
- return Optional.empty();
- }
- private ObjectType() {
- super(Object.class);
- }
- }
- public class BooleanType extends ClassType<Boolean> {
- private static final Set<Object> FALSE = Set.of(0, 0.0, JSONObject.NULL);
- public static final BooleanType BOOLEAN = new BooleanType();
- @NonNull @Override
- protected Optional<Object> doConversion(@Nullable Object value, @NonNull Source source) {
- return Optional.of("true".equalsIgnoreCase(String.valueOf(value));
- }
- private BooleanType() {
- super(Boolean.class);
- }
- }
- public class NumberType<N> extends ClassType<N> {
- private final Function<String, N> callback;
- public static final NumberType<Double> REAL = new NumberType(Double.class, Double::valueOf);
- public static final NumberType<Integer> INTEGER = new NumberType(Integer.class, Integer::decode);
- @NonNull @Override
- protected Optional<Object> doConversion(@Nullable Object value, @NonNull Source source) {
- if (value instanceof Collection) {
- final var collection = (Collection<?>) value;
- return Optional.of(collection.size());
- }
- if (value instanceof Map) {
- final var map = (Map<?, ?>) value;
- return Optional.of(map.size());
- }
- if (value instanceof String) {
- try {
- return callback.apply((String) value);
- } catch (final NumberFormatException ignored) {
- return Optional.empty();
- }
- }
- }
- private NumberType(Class<N> clazz, Function<String, N> callback) {
- super(Integer.class);
- this.callback = callback;
- }
- }
- public class StringType extends ClassType<String> {
- public static final StringType STRING = new StringType();
- @NonNull @Override
- protected Optional<Object> doConversion(@Nullable Object value, @NonNull Source source) {
- if (value == null || value == JSONObject.NULL) {
- return Optional.empty();
- }
- return Optional.of(value.toString());
- }
- private StringType() {
- super(String.class);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement