Skip to content

Commit

Permalink
Added better adapters, removed ansi dep
Browse files Browse the repository at this point in the history
  • Loading branch information
LatvianModder committed Oct 12, 2024
1 parent f7cb7b0 commit 518fa01
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 68 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ repositories {
}

dependencies {
implementation('dev.latvian.apps:ansi:1.0.0-build.1')
implementation('dev.latvian.apps:tiny-java-server:1.0.0-build.7')
compileOnly('dev.latvian.apps:tiny-java-server:1.0.0-build.7')
compileOnly('org.jetbrains:annotations:24.0.1')
testImplementation('dev.latvian.apps:ansi:1.0.0-build.1')
testImplementation('org.junit.jupiter:junit-jupiter-api:5.10.0')
testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.10.0')
testImplementation('junit:junit:4.13.2')
Expand Down
24 changes: 4 additions & 20 deletions src/main/java/dev/latvian/apps/json/JSON.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.latvian.apps.json;

import dev.latvian.apps.json.adapter.JSONAdapter;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
Expand All @@ -15,14 +16,10 @@
import java.nio.file.Path;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;

Expand Down Expand Up @@ -297,11 +294,12 @@ public JSON child() {
return new JSON(this);
}

@ApiStatus.Internal
@SuppressWarnings("unchecked")
public <T> T adapt(Object value, Type genericType) {
var t = genericType == null ? null : JSONAdapter.getRawType(genericType);

if (value == null) {
if (value == null || value == NULL) {
return t == Optional.class ? (T) Optional.empty() : null;
} else if (t == null || t == Object.class || t.isInstance(value)) {
return (T) value;
Expand Down Expand Up @@ -333,22 +331,8 @@ public <T> T adapt(Object value, Type genericType) {
return (T) Float.valueOf(((Number) value).floatValue());
} else if (t == Double.class || t == Double.TYPE) {
return (T) Double.valueOf(((Number) value).doubleValue());
} else if (t == Map.class || t == JSONObject.class) {
} else if (t == JSONObject.class || t == JSONArray.class) {
return (T) value;
} else if (t == List.class || t == Collection.class || t == Iterable.class || t == JSONArray.class) {
return (T) value;
} else if (t == Set.class) {
return (T) new HashSet<>((Collection<?>) value);
} else if (t.isEnum()) {
var str = String.valueOf(value);

for (var e : t.getEnumConstants()) {
if (e.toString().equalsIgnoreCase(str)) {
return (T) e;
}
}

throw new IllegalArgumentException("Unknown enum constant: " + str);
} else {
return (T) getAdapter(t).adapt(this, value, genericType);
}
Expand Down
25 changes: 6 additions & 19 deletions src/main/java/dev/latvian/apps/json/adapter/ArrayJSONAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.Type;
import java.util.Collection;

public class ArrayJSONAdapter implements JSONAdapter<Object> {
private final Class<?> component;
Expand All @@ -20,40 +19,28 @@ public class ArrayJSONAdapter implements JSONAdapter<Object> {

@Override
public Object adapt(JSON json, Object jsonValue, Type genericType) {
if (jsonValue instanceof Iterable<?> itr) {
int size;

if (itr instanceof Collection<?> c) {
size = c.size();
} else {
size = 0;

for (var ignored : itr) {
size++;
}
}

if (size == 0) {
if (jsonValue instanceof JSONArray jsonArray) {
if (jsonArray.isEmpty()) {
if (emptyArray == null) {
emptyArray = Array.newInstance(component, 0);
}

return emptyArray;
}

var arr = Array.newInstance(component, size);
var arr = Array.newInstance(component, jsonArray.size());

int i = 0;

for (var value : itr) {
for (var value : jsonArray) {
Array.set(arr, i, json.adapt(value, component));
i++;
}

return arr;
} else {
throw new IllegalArgumentException("Expected collection, got " + jsonValue.getClass().getName());
}

throw new IllegalArgumentException("Expected JSON array for array of '" + component.getName() + "'");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package dev.latvian.apps.json.adapter;

import dev.latvian.apps.json.JSON;
import dev.latvian.apps.json.JSONArray;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;

public class CollectionJSONAdapter implements JSONAdapter<Iterable<?>> {
public interface Factory {
Factory LIST = ArrayList::new;
Factory LINKED_LIST = s -> new LinkedList<>();
Factory LINKED_SET = LinkedHashSet::new;

Collection<?> create(int size);
}

private final Class<?> type;
private final Factory factory;

CollectionJSONAdapter(Class<?> type, Factory factory) {
this.type = type;
this.factory = factory;
}

@Override
public Iterable<?> adapt(JSON json, Object jsonValue, Type genericType) {
if (jsonValue instanceof JSONArray jsonArray) {
var collection = factory.create(jsonArray.size());
var valueType = genericType instanceof ParameterizedType pt ? pt.getActualTypeArguments()[0] : null;

for (var value : jsonArray) {
collection.add(json.adapt(value, valueType));
}

return collection;
}

throw new IllegalArgumentException("Expected JSON array for type '" + type.getName() + "'");
}
}
27 changes: 17 additions & 10 deletions src/main/java/dev/latvian/apps/json/adapter/EnumJSONAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
import java.util.Locale;

public class EnumJSONAdapter implements JSONAdapter<Object> {
public interface CustomName {
String getJSONName();
}

private record EnumValue(int index, String name, Object value) {
}

Expand All @@ -24,7 +28,7 @@ private EnumValue[] values() {
values = new EnumValue[c.length];

for (int i = 0; i < c.length; i++) {
values[i] = new EnumValue(i, ((Enum<?>) c[i]).name().toLowerCase(Locale.ROOT), c[i]);
values[i] = new EnumValue(i, c[i] instanceof CustomName cn ? cn.getJSONName() : ((Enum<?>) c[i]).name().toLowerCase(Locale.ROOT), c[i]);
}
}

Expand All @@ -33,6 +37,16 @@ private EnumValue[] values() {

@Override
public Object adapt(JSON json, Object jsonValue, Type genericType) {
if (jsonValue instanceof Number n) {
int i = n.intValue();

if (i >= 0 && i < values().length) {
return values()[i].value;
} else {
throw new IndexOutOfBoundsException("Index out of bounds: " + i);
}
}

var str = String.valueOf(jsonValue);

for (var val : values()) {
Expand All @@ -41,18 +55,11 @@ public Object adapt(JSON json, Object jsonValue, Type genericType) {
}
}

throw new NullPointerException("Eunm value '" + str + "' not found");
throw new NullPointerException("Unknown enum constant: " + str);
}

@Override
public void write(JSON json, Writer writer, Object value, int depth, boolean pretty) throws IOException {
for (var val : values()) {
if (val.value == value) {
json.write(writer, val.name, depth, pretty);
return;
}
}

json.write(writer, "", depth, pretty);
json.write(writer, value instanceof CustomName cn ? cn.getJSONName() : ((Enum<?>) value).name().toLowerCase(Locale.ROOT), depth, pretty);
}
}
43 changes: 34 additions & 9 deletions src/main/java/dev/latvian/apps/json/adapter/JSONAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,26 @@
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SequencedCollection;
import java.util.SequencedSet;
import java.util.Set;

public interface JSONAdapter<T> {
T adapt(JSON json, Object jsonValue, Type genericType);

void write(JSON json, Writer writer, T value, int depth, boolean pretty) throws IOException;
default void write(JSON json, Writer writer, T value, int depth, boolean pretty) throws IOException {
json.write(writer, value, depth, pretty);
}

static Class<?> getRawType(Type type) {
return switch (type) {
Expand All @@ -24,15 +39,25 @@ static Class<?> getRawType(Type type) {
};
}

static JSONAdapter<?> create(Class<?> type) {
if (type.isArray()) {
return new ArrayJSONAdapter(type.getComponentType());
} else if (type.isRecord()) {
return new RecordJSONAdapter(type);
} else if (type.isEnum()) {
return new EnumJSONAdapter(type);
static JSONAdapter<?> create(Class<?> t) {
if (t == IdentityHashMap.class) {
return new MapJSONAdapter(t, MapJSONAdapter.Factory.IDENTITY_MAP);
} else if (t == Map.class || t == HashMap.class || t == LinkedHashMap.class) {
return new MapJSONAdapter(t, MapJSONAdapter.Factory.MAP);
} else if (t == Set.class || t == SequencedSet.class || t == HashSet.class || t == LinkedHashSet.class) {
return new CollectionJSONAdapter(t, CollectionJSONAdapter.Factory.LINKED_SET);
} else if (t == LinkedList.class) {
return new CollectionJSONAdapter(t, CollectionJSONAdapter.Factory.LINKED_LIST);
} else if (t == List.class || t == ArrayList.class || t == Collection.class || t == Iterable.class || t == SequencedCollection.class) {
return new CollectionJSONAdapter(t, CollectionJSONAdapter.Factory.LIST);
} else if (t.isArray()) {
return new ArrayJSONAdapter(t.getComponentType());
} else if (t.isRecord()) {
return new RecordJSONAdapter(t);
} else if (t.isEnum()) {
return new EnumJSONAdapter(t);
} else {
return new ReflectionJSONAdapter(type);
return new ReflectionJSONAdapter(t);
}
}
}
43 changes: 43 additions & 0 deletions src/main/java/dev/latvian/apps/json/adapter/MapJSONAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package dev.latvian.apps.json.adapter;

import dev.latvian.apps.json.JSON;
import dev.latvian.apps.json.JSONObject;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class MapJSONAdapter implements JSONAdapter<Map<String, ?>> {
public interface Factory {
Factory MAP = LinkedHashMap::new;
Factory IDENTITY_MAP = IdentityHashMap::new;

Map<String, ?> create(int size);
}

private final Class<?> type;
private final Factory factory;

MapJSONAdapter(Class<?> type, Factory factory) {
this.type = type;
this.factory = factory;
}

@Override
public Map<String, ?> adapt(JSON json, Object jsonValue, Type genericType) {
if (jsonValue instanceof JSONObject jsonObject) {
var map = factory.create(jsonObject.size());
var valueType = genericType instanceof ParameterizedType pt ? pt.getActualTypeArguments()[1] : null;

for (var entry : jsonObject.entrySet()) {
map.put(entry.getKey(), json.adapt(entry.getValue(), valueType));
}

return map;
}

throw new IllegalArgumentException("Expected JSON object for type '" + type.getName() + "'");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;

public class RecordJSONAdapter implements JSONAdapter<Object> {
Expand Down Expand Up @@ -47,7 +46,7 @@ private JSONRecordComponent[] components() {

@Override
public Object adapt(JSON json, Object jsonValue, Type genericType) {
if (jsonValue instanceof Map<?, ?> object) {
if (jsonValue instanceof JSONObject jsonObject) {
var components = components();

if (constructor == null) {
Expand All @@ -62,7 +61,7 @@ public Object adapt(JSON json, Object jsonValue, Type genericType) {
var args = new Object[components.length];

for (int i = 0; i < args.length; i++) {
var j = object.get(components[i].name);
var j = jsonObject.get(components[i].name);

if (j != null && j != JSON.NULL) {
if (components[i].optional) {
Expand Down Expand Up @@ -97,7 +96,7 @@ public void write(JSON json, Writer writer, Object value, int depth, boolean pre
if (op.isPresent()) {
obj.put(rc.name, op.get());
}
} else {
} else if (o != null) {
obj.put(rc.name, o);
}
} catch (Exception ex) {
Expand Down
Loading

0 comments on commit 518fa01

Please sign in to comment.