diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 0000000..3804398 --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + xyz.bobkinn + indigodataio-parent + 3.0.0 + + + indigodataio + + + 17 + 17 + UTF-8 + + + + + org.jetbrains + annotations + 24.0.1 + + + org.projectlombok + lombok + 1.18.26 + provided + + + junit + junit + 4.10 + test + + + + \ No newline at end of file diff --git a/core/src/main/java/xyz/bobkinn/indigodataio/DataHolder.java b/core/src/main/java/xyz/bobkinn/indigodataio/DataHolder.java new file mode 100644 index 0000000..cc9e0ec --- /dev/null +++ b/core/src/main/java/xyz/bobkinn/indigodataio/DataHolder.java @@ -0,0 +1,555 @@ +package xyz.bobkinn.indigodataio; + +import xyz.bobkinn.indigodataio.ops.MapOps; +import xyz.bobkinn.indigodataio.ops.TypeOps; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Main interface for data holders.
+ * Data types:
+ * + * And all of this has {@link List} and array getters and setter too + * @param implementation type + * @param

parent class type, like Object from {@link NestedKeyMap}, JsonElement from GSON or Tag from NBT.
+ * Meant to be internal type that used as value in internal object + */ +@SuppressWarnings({"unused", "UnusedReturnValue"}) +public interface DataHolder, P> { + + @SafeVarargs + static List toList(T... array){ + if (array == null) return null; + return List.of(array); + } + + @SuppressWarnings("unchecked") + static T[] toArray(List c){ + if (c == null) return null; + return (T[]) c.toArray(); + } + + T getNew(); + + /** + * @return new builder + */ + MapBuilder toBuilder(); + + /** + * Get a builder view on this key + * @param key key + * @return new builder + */ + MapBuilder toBuilder(String key); + + TypeOps

getOps(); + + P remove(String key); + + boolean contains(String key); + + boolean contains(String key, Class type); + + boolean containsSection(String key); + + Set keys(); + + Set keys(String key); + + int size(); + + void clear(); + + default List getDefaultList(){ + return null; + } + + /* TYPE ARGS AND MAPS METHODS */ + + // getters + + P get(String key, P def); + + P get(String key); + + List getList(String key, List def); + + default List getList(String key) { + return getList(key, getDefaultList()); + } + + default P[] getArray(String key, P[] def) { + return toArray(getList(key, toList(def))); + } + + default P[] getArray(String key){ + return toArray(getList(key)); + } + + // setters + + /** + * Puts java value in this map + * @param key key + * @param value value that can be converted using {@link MapOps#convertTo(TypeOps, Object)} + * @return previous value + */ + default P put(String key, Object value){ + return putValue(key, MapOps.INSTANCE.convertTo(getOps(), value)); + } + + /** + * Puts type value that extends P. When P is Object then {@link #put(String, Object)} is recommended. + * Or use precise methods for accurate value storing + * @param key key + * @param value value that extends P + * @return previous value + */ + P putValue(String key, P value); + + default P putList(String key, List value){ + return putValue(key, getOps().createList(value.stream())); + } + + default P putArray(String key, P[] value) { + return putValue(key, getOps().createList(Arrays.stream(value))); + } + + /* SECTION METHODS */ + + // getters + + T getSection(String key, T def); + + T getSection(String key); + + List getSectionList(String key, List def); + + default List getSectionList(String key){ + return getSectionList(key, getDefaultList()); + } + + default T[] getSectionArray(String key, T[] def){ + return toArray(getSectionList(key, toList(def))); + } + + default T[] getSectionArray(String key){ + return toArray(getSectionList(key)); + } + + // setters + + P putSection(String key, T value); + + P putSectionList(String key, List value); + + default P putSectionArray(String key, T[] value){ + return putSectionList(key, toList(value)); + } + + /* MAP METHODS */ + + // getters + + Map getMap(String key, Map def); + + Map getMap(String key); + + List> getMapList(String key, List> def); + + default List> getMapList(String key){ + return getMapList(key, getDefaultList()); + } + + default Map[] getMapArray(String key, Map[] def) { + //noinspection unchecked + return (Map[]) getMapList(key, toList(def)).toArray(Map[]::new); + } + + default Map[] getMapArray(String key){ + //noinspection unchecked + return (Map[]) getMapList(key).toArray(Map[]::new); + } + + // setters + + P putMap(String key, Map value); + + /** + * Puts map but all values are converted using {@link MapOps#convertMap(TypeOps, Object)} + * @param key key + * @param value map + * @return previous value + */ + default P putRawMap(String key, Map value){ + return putValue(key, MapOps.INSTANCE.convertMap(getOps(), value)); + } + + P putMapList(String key, List> value); + + default P putMapArray(String key, Map[] value) { + return putMapList(key, toList(value)); + } + + // + + /* String methods */ + + // getters + + default String getString(String key, String def){ + return getOps().getString(get(key)).orElse(def); + } + + default String getString(String key){ + return getString(key, null); + } + + default List getStringList(String key, List def) { + return getOps().getStringList(get(key)).orElse(def); + } + + default List getStringList(String key){ + return getStringList(key, getDefaultList()); + } + + default String[] getStringArray(String key, String[] def){ + return getOps().getStringArray(get(key)).orElse(def); + } + + default String[] getStringArray(String key){ + return getStringArray(key, null); + } + + // setters + + default P putString(String key, String value){ + return putValue(key, getOps().createString(value)); + } + + default P putStringList(String key, List value){ + return putValue(key, getOps().createStringList(value)); + } + + default P putStringArray(String key, String[] value){ + return putValue(key, getOps().createStringArray(value)); + } + + /* Bool methods */ + + // getters + + default boolean getBoolean(String key, boolean def) { + return getOps().getBoolean(get(key)).orElse(def); + } + + default boolean getBoolean(String key){ + return getBoolean(key, false); + } + + default List getBoolList(String key, List def){ + return getOps().getBoolList(get(key)).orElse(def); + } + + default List getBoolList(String key){ + return getBoolList(key, getDefaultList()); + } + + default boolean[] getBoolArray(String key, boolean[] def){ + return getOps().getBoolArray(get(key)).orElse(def); + } + + default boolean[] getBoolArray(String key){ + return getBoolArray(key, null); + } + + // setters + + default P putBoolean(String key, boolean value){ + return putValue(key, getOps().createBoolean(value)); + } + + default P putBoolList(String key, List value){ + return putValue(key, getOps().createBoolList(value)); + } + + default P putBoolArray(String key, boolean[] value){ + return putValue(key, getOps().createBoolArray(value)); + } + + + /* Byte methods */ + + // getters + + default byte getByte(String key, byte def) { + return getOps().getByte(get(key)).orElse(def); + } + + default byte getByte(String key){ + return getByte(key, (byte) 0); + } + + default List getByteList(String key, List def){ + return getOps().getByteList(get(key)).orElse(def); + } + + default List getByteList(String key){ + return getByteList(key, getDefaultList()); + } + + default byte[] getByteArray(String key, byte[] def){ + return getOps().getByteArray(get(key)).orElse(def); + } + + default byte[] getByteArray(String key){ + return getByteArray(key, null); + } + + // setters + + default P putByte(String key, byte value){ + return putValue(key, getOps().createByte(value)); + } + + default P putByteList(String key, List value){ + return putValue(key, getOps().createByteList(value)); + } + + default P putByteArray(String key, byte[] value){ + return putValue(key, getOps().createByteArray(value)); + } + + + /* Short methods */ + + // getters + + default short getShort(String key, short def){ + return getOps().getShort(get(key)).orElse(def); + } + + default short getShort(String key) { + return getShort(key, (short) 0); + } + + default List getShortList(String key, List def){ + return getOps().getShortList(get(key)).orElse(def); + } + + default List getShortList(String key){ + return getShortList(key, getDefaultList()); + } + + default short[] getShortArray(String key, short[] def){ + return getOps().getShortArray(get(key)).orElse(def); + } + + default short[] getShortArray(String key){ + return getShortArray(key,null); + } + + // setters + + default P putShort(String key, short value){ + return putValue(key, getOps().createShort(value)); + } + + default P putShortList(String key, List value){ + return putValue(key, getOps().createShortList(value)); + } + + default P putShortArray(String key, short[] value){ + return putValue(key, getOps().createShortArray(value)); + } + + + /* Integer methods */ + + // getters + + default int getInt(String key, int def){ + return getOps().getInt(get(key)).orElse(def); + } + + default int getInt(String key) { + return getInt(key, 0); + } + + default List getIntList(String key, List def){ + return getOps().getIntList(get(key)).orElse(def); + } + + default List getIntList(String key){ + return getIntList(key, getDefaultList()); + } + + default int[] getIntArray(String key, int[] def){ + return getOps().getIntArray(get(key)).orElse(def); + } + + default int[] getIntArray(String key){ + return getIntArray(key, null); + } + + // setters + + default P putInt(String key, int value){ + return putValue(key, getOps().createInt(value)); + } + + default P putIntList(String key, List value){ + return putValue(key, getOps().createIntList(value)); + } + + default P putIntArray(String key, int[] value){ + return putValue(key, getOps().createIntArray(value)); + } + + + /* Long methods */ + + // getters + + default long getLong(String key, long def){ + return getOps().getLong(get(key)).orElse(def); + } + + default long getLong(String key) { + return getLong(key, 0L); + } + + default List getLongList(String key, List def){ + return getOps().getLongList(get(key)).orElse(def); + } + + default List getLongList(String key){ + return getLongList(key, getDefaultList()); + } + + default long[] getLongArray(String key, long[] def){ + return getOps().getLongArray(get(key)).orElse(def); + } + + default long[] getLongArray(String key){ + return getLongArray(key, null); + } + + // setters + + default P putLong(String key, long value){ + return putValue(key, getOps().createLong(value)); + } + + default P putLongList(String key, List value){ + return putValue(key, getOps().createLongList(value)); + } + + default P putLongArray(String key, long[] value){ + return putValue(key, getOps().createLongArray(value)); + } + + + /* Float methods */ + + // getters + + default float getFloat(String key, float def){ + return getOps().getFloat(get(key)).orElse(def); + } + + default float getFloat(String key) { + return getFloat(key, 0); + } + + default List getFloatList(String key, List def){ + return getOps().getFloatList(get(key)).orElse(def); + } + + default List getFloatList(String key){ + return getFloatList(key, getDefaultList()); + } + + default float[] getFloatArray(String key, float[] def){ + return getOps().getFloatArray(get(key)).orElse(def); + } + + default float[] getFloatArray(String key){ + return getFloatArray(key, null); + } + + // setters + + default P putFloat(String key, float value){ + return putValue(key, getOps().createFloat(value)); + } + + default P putFloatList(String key, List value){ + return putValue(key, getOps().createFloatList(value)); + } + + default P putFloatArray(String key, float[] value){ + return putValue(key, getOps().createFloatArray(value)); + } + + + /* Double methods */ + + // getters + + default double getDouble(String key, double def){ + return getOps().getDouble(get(key)).orElse(def); + } + + default double getDouble(String key) { + return getDouble(key, 0); + } + + default List getDoubleList(String key, List def){ + return getOps().getDoubleList(get(key)).orElse(def); + } + + default List getDoubleList(String key){ + return getDoubleList(key, getDefaultList()); + } + + default double[] getDoubleArray(String key, double[] def){ + return getOps().getDoubleArray(get(key)).orElse(def); + } + + default double[] getDoubleArray(String key){ + return getDoubleArray(key, null); + } + + // setters + + default P putDouble(String key, double value){ + return putValue(key, getOps().createDouble(value)); + } + + default P putDoubleList(String key, List value){ + return putValue(key, getOps().createDoubleList(value)); + } + + default P putDoubleArray(String key, double[] value){ + return putValue(key, getOps().createDoubleArray(value)); + } + + // + +} diff --git a/src/main/java/xyz/bobkinn/indigodataio/ImmutablePair.java b/core/src/main/java/xyz/bobkinn/indigodataio/ImmutablePair.java similarity index 100% rename from src/main/java/xyz/bobkinn/indigodataio/ImmutablePair.java rename to core/src/main/java/xyz/bobkinn/indigodataio/ImmutablePair.java diff --git a/src/main/java/xyz/bobkinn/indigodataio/MapBuilder.java b/core/src/main/java/xyz/bobkinn/indigodataio/MapBuilder.java similarity index 92% rename from src/main/java/xyz/bobkinn/indigodataio/MapBuilder.java rename to core/src/main/java/xyz/bobkinn/indigodataio/MapBuilder.java index 0ece54a..9b02de0 100644 --- a/src/main/java/xyz/bobkinn/indigodataio/MapBuilder.java +++ b/core/src/main/java/xyz/bobkinn/indigodataio/MapBuilder.java @@ -38,7 +38,7 @@ public MapBuilder put(String key, T value){ * @return builder on this level */ public MapBuilder put(String key, P value){ - holder.put(key, value); + holder.putValue(key, value); return this; } @@ -49,7 +49,7 @@ public MapBuilder put(String key, P value){ * @return builder on this level */ public MapBuilder put(String key, Number value){ - holder.put(key, numberConv.apply(value)); + holder.putValue(key, numberConv.apply(value)); return this; } @@ -60,7 +60,7 @@ public MapBuilder put(String key, Number value){ * @return builder on this level */ public MapBuilder put(String key, Boolean value){ - holder.put(key, boolConv.apply(value)); + holder.putValue(key, boolConv.apply(value)); return this; } @@ -71,7 +71,7 @@ public MapBuilder put(String key, Boolean value){ * @return builder on this level */ public MapBuilder put(String key, String value){ - holder.put(key, strConv.apply(value)); + holder.putValue(key, strConv.apply(value)); return this; } diff --git a/src/main/java/xyz/bobkinn/indigodataio/NestedKeyMap.java b/core/src/main/java/xyz/bobkinn/indigodataio/NestedKeyMap.java similarity index 52% rename from src/main/java/xyz/bobkinn/indigodataio/NestedKeyMap.java rename to core/src/main/java/xyz/bobkinn/indigodataio/NestedKeyMap.java index ab2ed37..ffd6e57 100644 --- a/src/main/java/xyz/bobkinn/indigodataio/NestedKeyMap.java +++ b/core/src/main/java/xyz/bobkinn/indigodataio/NestedKeyMap.java @@ -4,9 +4,10 @@ import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import xyz.bobkinn.indigodataio.ops.MapOps; +import xyz.bobkinn.indigodataio.ops.TypeOps; import java.util.*; -import java.util.function.Function; @RequiredArgsConstructor public class NestedKeyMap extends AbstractMap implements DataHolder{ @@ -30,6 +31,11 @@ public NestedKeyMap getNew() { return new NestedKeyMap(); } + @Override + public TypeOps getOps() { + return MapOps.INSTANCE; + } + @Override public MapBuilder toBuilder() { return new MapBuilder<>(null, this, NestedKeyMap::new, @@ -155,32 +161,6 @@ public Set> entrySet() { return data.entrySet(); } - public List getNumberList(String key, Function f, List def){ - var v = get(key); - if (v == null) return def; - try { - if (v instanceof List l){ - //noinspection unchecked - return ((List) l).stream().map(f).toList(); - } else if (v instanceof int[] l) { - return Arrays.stream(l).boxed().map(f).toList(); - } else if (v instanceof float[] l) { - return NumberUtil.floatToStream(l).map(f).toList(); - } else if (v instanceof short[] l) { - return NumberUtil.shortToStream(l).map(f).toList(); - } else if (v instanceof double[] l) { - return Arrays.stream(l).boxed().map(f).toList(); - } else if (v instanceof long[] l) { - return Arrays.stream(l).boxed().map(f).toList(); - } else if (v instanceof byte[] l) { - return NumberUtil.byteToList(l).stream().map(f).toList(); - } - return def; - } catch (ClassCastException ignored){ - return def; - } - } - @Override public Object get(String key, Object def) { var p = extractMapKey(key); @@ -204,6 +184,11 @@ public List getList(String key, List def) { @Override public Object put(String key, Object value) { + return DataHolder.super.put(key, value); + } + + @Override + public Object putValue(String key, Object value) { if (value instanceof NestedKeyMap){ throw new IllegalArgumentException("Use putSection to put NestedKeyMap"); } @@ -214,7 +199,7 @@ public Object put(String key, Object value) { @Override public Object putList(String key, List value) { - return put(key, value); + return putValue(key, value); } @Override @@ -238,16 +223,16 @@ public List getSectionList(String key, List def) { @Override public Object putSection(String key, NestedKeyMap value) { - return put(key, value != null ? value.data : null); + return putValue(key, value != null ? value.data : null); } @Override public Object putSectionList(String key, List value) { Object old; if (value == null) { - old = put(key, null); + old = putValue(key, null); } else { - old = put(key, value.stream().map(NestedKeyMap::getRaw).toList()); + old = putValue(key, value.stream().map(NestedKeyMap::getRaw).toList()); } return old; } @@ -281,213 +266,12 @@ public List> getMapList(String key, List @Override public Object putMap(String key, Map value) { - return put(key, value); + return putValue(key, value); } @Override public Object putMapList(String key, List> value) { - return put(key, value); - } - - @Override - public String getString(String key, String def) { - var v = get(key); - if (v instanceof String s) return s; - else return def; - } - - @Override - public String getString(String key) { - return getString(key, null); - } - - @Override - public List getStringList(String key, List def) { - var v = get(key, def); - try { - //noinspection unchecked - return (List) v; - } catch (Exception e) { - return def; - } - } - - @Override - public Object putString(String key, String value) { - return put(key, value); - } - - @Override - public Object putStringList(String key, List value) { - return put(key, value); - } - - @Override - public byte getByte(String key, byte def) { - var v = get(key); - if (v == null) return def; - if (v instanceof Number n) return n.byteValue(); - return def; - } - - @Override - public byte getByte(String key) { - return getByte(key, (byte) 0); - } - - @Override - public List getByteList(String key, List def) { - return getNumberList(key, Number::byteValue, def); - } - - @Override - public Object putByte(String key, byte value) { - return put(key, value); - } - - @Override - public Object putByteList(String key, List value) { - return put(key, value); - } - - @Override - public short getShort(String key, short def) { - var v = get(key); - if (v == null) return def; - if (v instanceof Number n) return n.shortValue(); - return def; - } - - @Override - public short getShort(String key) { - return getShort(key, (short) 0); - } - - @Override - public List getShortList(String key, List def) { - return getNumberList(key, Number::shortValue, def); - } - - @Override - public Object putShort(String key, short value) { - return put(key, value); - } - - @Override - public Object putShortList(String key, List value) { - return put(key, value); - } - - @Override - public int getInt(String key, int def) { - var v = get(key); - if (v == null) return def; - if (v instanceof Number n) return n.intValue(); - return def; - } - - @Override - public int getInt(String key) { - return getInt(key, 0); - } - - @Override - public List getIntList(String key, List def) { - return getNumberList(key, Number::intValue, def); - } - - @Override - public Object putInt(String key, int value) { - return put(key, value); - } - - @Override - public Object putIntList(String key, List value) { - return put(key, value); - } - - @Override - public long getLong(String key, long def) { - var v = get(key); - if (v == null) return def; - if (v instanceof Number n) return n.longValue(); - return def; - } - - @Override - public long getLong(String key) { - return getLong(key, 0L); - } - - @Override - public List getLongList(String key, List def) { - return getNumberList(key, Number::longValue, def); - } - - @Override - public Object putLong(String key, long value) { - return put(key, value); - } - - @Override - public Object putLongList(String key, List value) { - return put(key, value); - } - - @Override - public float getFloat(String key, float def) { - var v = get(key); - if (v == null) return def; - if (v instanceof Number n) return n.floatValue(); - return def; - } - - @Override - public float getFloat(String key) { - return getFloat(key, 0); - } - - @Override - public List getFloatList(String key, List def) { - return getNumberList(key, Number::floatValue, def); - } - - @Override - public Object putFloat(String key, float value) { - return put(key, value); - } - - @Override - public Object putFloatList(String key, List value) { - return put(key, value); - } - - @Override - public double getDouble(String key, double def) { - var v = get(key); - if (v == null) return def; - if (v instanceof Number n) return n.doubleValue(); - return def; - } - - @Override - public double getDouble(String key) { - return getDouble(key, 0); - } - - @Override - public List getDoubleList(String key, List def) { - return getNumberList(key, Number::doubleValue, def); - } - - @Override - public Object putDouble(String key, double value) { - return put(key, value); - } - - @Override - public Object putDoubleList(String key, List value) { - return put(key, value); + return putValue(key, value); } } diff --git a/src/main/java/xyz/bobkinn/indigodataio/NumberUtil.java b/core/src/main/java/xyz/bobkinn/indigodataio/NumberUtil.java similarity index 94% rename from src/main/java/xyz/bobkinn/indigodataio/NumberUtil.java rename to core/src/main/java/xyz/bobkinn/indigodataio/NumberUtil.java index 5e7b249..1065671 100644 --- a/src/main/java/xyz/bobkinn/indigodataio/NumberUtil.java +++ b/core/src/main/java/xyz/bobkinn/indigodataio/NumberUtil.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.stream.Stream; +@SuppressWarnings("unused") public class NumberUtil { public static @NotNull Stream floatToStream(float @NotNull [] ls){ var ret = new ArrayList(ls.length); @@ -19,6 +20,12 @@ public class NumberUtil { return ret.stream(); } + public static @NotNull Stream boolToStream(boolean @NotNull [] ls){ + var ret = new ArrayList(ls.length); + for (var f : ls) ret.add(f); + return ret.stream(); + } + public static List byteToList(byte[] v){ if (v == null) return null; var ret = new Byte[v.length]; diff --git a/src/main/java/xyz/bobkinn/indigodataio/Pair.java b/core/src/main/java/xyz/bobkinn/indigodataio/Pair.java similarity index 84% rename from src/main/java/xyz/bobkinn/indigodataio/Pair.java rename to core/src/main/java/xyz/bobkinn/indigodataio/Pair.java index b13a955..b857ab0 100644 --- a/src/main/java/xyz/bobkinn/indigodataio/Pair.java +++ b/core/src/main/java/xyz/bobkinn/indigodataio/Pair.java @@ -7,6 +7,8 @@ import java.io.Serializable; import java.util.Map; import java.util.Objects; +import java.util.stream.Collector; +import java.util.stream.Collectors; /** * Copy of org.apache.commons.lang3.tuple.Pair @@ -47,6 +49,21 @@ public boolean equals(Object obj) { return false; } + public L getFirst(){ + return getLeft(); + } + + public R getSecond(){ + return getRight(); + } + public static Collector, ?, Map> toMap() { + return Collectors.toMap(Pair::getFirst, Pair::getSecond); + } + + public Map.Entry toEntry(){ + return Map.entry(getKey(), getValue()); + } + // Map.Entry @Override diff --git a/core/src/main/java/xyz/bobkinn/indigodataio/ops/BaseMap.java b/core/src/main/java/xyz/bobkinn/indigodataio/ops/BaseMap.java new file mode 100644 index 0000000..fc8c993 --- /dev/null +++ b/core/src/main/java/xyz/bobkinn/indigodataio/ops/BaseMap.java @@ -0,0 +1,44 @@ +package xyz.bobkinn.indigodataio.ops; + +import org.jetbrains.annotations.Nullable; +import xyz.bobkinn.indigodataio.Pair; + +import java.util.Map; +import java.util.stream.Stream; + +@SuppressWarnings("unused") +public interface BaseMap { + @Nullable + T get(final T key); + + @Nullable + T get(final String key); + + Stream> entries(); + + static BaseMap forMap(final Map map, final TypeOps ops) { + return new BaseMap<>() { + @Nullable + @Override + public T get(final T key) { + return map.get(key); + } + + @Nullable + @Override + public T get(final String key) { + return get(ops.createString(key)); + } + + @Override + public Stream> entries() { + return map.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue())); + } + + @Override + public String toString() { + return "BaseMap[" + map + "]"; + } + }; + } +} diff --git a/core/src/main/java/xyz/bobkinn/indigodataio/ops/MapOps.java b/core/src/main/java/xyz/bobkinn/indigodataio/ops/MapOps.java new file mode 100644 index 0000000..3bab5ee --- /dev/null +++ b/core/src/main/java/xyz/bobkinn/indigodataio/ops/MapOps.java @@ -0,0 +1,211 @@ +package xyz.bobkinn.indigodataio.ops; + +import org.jetbrains.annotations.Nullable; +import xyz.bobkinn.indigodataio.Pair; + +import java.nio.*; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + + +/** + * Type operations used in {@link xyz.bobkinn.indigodataio.NestedKeyMap}. Converts arrays to lists + */ +public class MapOps implements TypeOps { + public static final MapOps INSTANCE = new MapOps(); + + @Override + public Object empty() { + return null; + } + + @Override + public U convertTo(final TypeOps outOps, final Object input) { + if (input == null) return outOps.empty(); + if (input instanceof Map) return convertMap(outOps, input); + if (input instanceof final byte[] value) return outOps.createByteArray(value); + if (input instanceof final short[] value) return outOps.createShortArray(value); + if (input instanceof final int[] value) return outOps.createIntArray(value); + if (input instanceof final long[] value) return outOps.createLongArray(value); + if (input instanceof final float[] value) return outOps.createFloatArray(value); + if (input instanceof final double[] value) return outOps.createDoubleArray(value); + if (input instanceof final Object[] value) + return outOps.createList(Arrays.stream(value).map(v -> convertTo(outOps, v))); + if (input instanceof ByteBuffer value) return outOps.createByteBuffer(value); + if (input instanceof ShortBuffer value) return outOps.createShortBuffer(value); + if (input instanceof IntStream value) return outOps.createIntStream(value); + if (input instanceof IntBuffer value) return outOps.createIntArray(value.array()); + if (input instanceof LongStream value) return outOps.createLongStream(value); + if (input instanceof LongBuffer value) return outOps.createLongArray(value.array()); + if (input instanceof FloatBuffer value) return outOps.createFloatBuffer(value); + if (input instanceof DoubleBuffer value) return outOps.createDoubleBuffer(value); + if (input instanceof List) return convertList(outOps, input); + if (input instanceof final String value) return outOps.createString(value); + if (input instanceof final Boolean value) return outOps.createBoolean(value); + if (input instanceof final Byte value) return outOps.createByte(value); + if (input instanceof final Short value) return outOps.createShort(value); + if (input instanceof final Integer value) return outOps.createInt(value); + if (input instanceof final Long value) return outOps.createLong(value); + if (input instanceof final Float value) return outOps.createFloat(value); + if (input instanceof final Double value) return outOps.createDouble(value); + if (input instanceof final Number value) return outOps.createNumeric(value); + throw new IllegalStateException("Don't know how to convert " + input); + } + + @Override + public Optional getNumberValue(Object input) { + if (input instanceof Number n) return Optional.of(n); + else return Optional.empty(); + } + + @Override + public Object createNumeric(Number i) { + return i; + } + + @Override + public Object createByte(byte value) { + return value; + } + + @Override + public Object createBoolean(boolean value) { + return value; + } + + @Override + public Object createShort(short value) { + return value; + } + + @Override + public Object createInt(int value) { + return value; + } + + @Override + public Object createLong(long value) { + return value; + } + + @Override + public Object createFloat(float value) { + return value; + } + + @Override + public Object createDouble(double value) { + return value; + } + + @Override + public Optional getString(Object input) { + if (input instanceof String s) return Optional.of(s); + else return Optional.empty(); + } + + @Override + public Object createString(String value) { + return value; + } + + @Override + public Optional mergeToList(Object list, Object value) { + return Optional.empty(); + } + + @Override + public Optional mergeToMap(Object map, Object key, Object value) { + return Optional.empty(); + } + + @Override + public Optional>> getMapValues(Object input) { + if (input instanceof final Map map) { + return Optional.of(map.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue()))); + } + return Optional.empty(); + } + + private static Stream> getMapEntries(final Map input) { + return input.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue())); + } + + @Override + public Optional>> getMapEntries(final Object input) { + if (input instanceof final Map map) { + return Optional.of(map::forEach); + } + return Optional.empty(); + } + + @Override + public Optional> getMap(Object input) { + if (input instanceof final Map map) { + return Optional.of( + new BaseMap<>() { + @Nullable + @Override + public Object get(final Object key) { + return map.get(key); + } + + @Nullable + @Override + public Object get(final String key) { + return map.get(key); + } + + @Override + public Stream> entries() { + return getMapEntries(map); + } + + @Override + public String toString() { + return "BaseMap[" + map + "]"; + } + } + ); + } + return Optional.empty(); + } + + @Override + public Object createMap(Stream> map) { + var ls = map.map(Pair::toEntry).toArray(Map.Entry[]::new); + return Map.ofEntries(ls); + } + + @Override + public Optional> getStream(Object input) { + if (input instanceof List ls) { + return Optional.of(ls.stream().map(o -> o)); + } + return Optional.empty(); + } + + @Override + public Object createList(Stream input) { + return input.toList(); + } + + @Override + public Object createArray(Object[] input) { + return createList(Arrays.stream(input)); + } + + @Override + public Object remove(Object input, String key) { + if (input instanceof final Map map) { + final Map result = new LinkedHashMap<>(map); + result.remove(key); + return Map.copyOf(result); + } + return input; + } +} diff --git a/core/src/main/java/xyz/bobkinn/indigodataio/ops/TypeOps.java b/core/src/main/java/xyz/bobkinn/indigodataio/ops/TypeOps.java new file mode 100644 index 0000000..d0688c1 --- /dev/null +++ b/core/src/main/java/xyz/bobkinn/indigodataio/ops/TypeOps.java @@ -0,0 +1,484 @@ +package xyz.bobkinn.indigodataio.ops; + +import xyz.bobkinn.indigodataio.NumberUtil; +import xyz.bobkinn.indigodataio.Pair; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +/** + * Class that provides conversations between data types. Based on DynamicOps from Mojang/DataFixerUpper + * @param data type + */ +@SuppressWarnings("unused") +public interface TypeOps { + T empty(); + + default T emptyMap() { + return createMap(Map.of()); + } + + default T emptyList() { + return createList(Stream.empty()); + } + + U convertTo(TypeOps outOps, T input); + + Optional getNumberValue(T input); + + default Number getNumberValue(final T input, final Number defaultValue) { + return getNumberValue(input).orElse(defaultValue); + } + + T createNumeric(Number i); + + // byte + + default T createByte(final byte value) { + return createNumeric(value); + } + + default Optional getByte(T input) { + return getNumberValue(input).map(Number::byteValue); + } + + // short + + default T createShort(final short value) { + return createNumeric(value); + } + + default Optional getShort(T input) { + return getNumberValue(input).map(Number::shortValue); + } + + // int + + default T createInt(final int value) { + return createNumeric(value); + } + + default Optional getInt(T input) { + return getNumberValue(input).map(Number::intValue); + } + + // long + + default T createLong(final long value) { + return createNumeric(value); + } + + default Optional getLong(T input) { + return getNumberValue(input).map(Number::longValue); + } + + // float + + default T createFloat(final float value) { + return createNumeric(value); + } + + default Optional getFloat(T input) { + return getNumberValue(input).map(Number::floatValue); + } + + // double + + default T createDouble(final double value) { + return createNumeric(value); + } + + default Optional getDouble(T input) { + return getNumberValue(input).map(Number::doubleValue); + } + + // boolean + + default T createBoolean(final boolean value) { + return createByte((byte) (value ? 1 : 0)); + } + + default Optional getBoolean(final T input) { + return getNumberValue(input).map(number -> number.byteValue() != 0); + } + + // end + + Optional getString(T input); + + T createString(String value); + + Optional mergeToList(T list, T value); + + default Optional mergeToList(final T list, final List values) { + Optional result = Optional.of(list); + for (final T value : values) { + result = result.flatMap(r -> mergeToList(r, value)); + } + return result; + } + + Optional mergeToMap(T map, T key, T value); + + default Optional mergeToMap(final T map, final Map values) { + return mergeToMap(map, BaseMap.forMap(values, this)); + } + + default Optional mergeToMap(final T map, final BaseMap values) { + final AtomicReference> result = new AtomicReference<>(Optional.of(map)); + + values.entries().forEach(entry -> + result.setPlain(result.getPlain().flatMap(r -> mergeToMap(r, entry.getFirst(), entry.getSecond()))) + ); + return result.getPlain(); + } + + default Optional mergeToPrimitive(final T prefix, final T value) { + if (!Objects.equals(prefix, empty())) { + return Optional.empty(); + } + return Optional.of(value); + } + + Optional>> getMapValues(T input); + + default Optional>> getMapEntries(final T input) { + return getMapValues(input).map(s -> c -> s.forEach(p -> c.accept(p.getFirst(), p.getSecond()))); + } + + T createMap(Stream> map); + + default Optional> getMap(final T input) { + return getMapValues(input).flatMap(s -> { + try { + return Optional.of(BaseMap.forMap(s.collect(Pair.toMap()), this)); + } catch (final IllegalStateException e) { + return Optional.empty(); + } + }); + } + + default T createMap(final Map map) { + return createMap(map.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue()))); + } + + Optional> getStream(T input); + + default Optional>> getList(final T input) { + return getStream(input).map(s -> s::forEach); + } + + T createList(Stream input); + + T createArray(T[] input); + + // string lists + + default Optional> getStringList(final T input){ + return getStream(input).flatMap(stream -> { + final List list = stream.toList(); + if (list.stream().allMatch(element -> getString(element).isPresent())) { + return Optional.of(list.stream().map(e -> getString(e).orElseThrow()).toList()); + } + return Optional.empty(); + }); + } + + default Optional getStringArray(final T input){ + return getStringList(input).map(l -> l.toArray(String[]::new)); + } + + default T createStringList(List value){ + return createList(value.stream().map(this::createString)); + } + + default T createStringArray(String[] value){ + return createStringList(Arrays.stream(value).toList()); + } + + // bool lists + + default Optional> getBoolList(final T input){ + return getStream(input).flatMap(stream -> { + final List list = stream.toList(); + if (list.stream().allMatch(element -> getBoolean(element).isPresent())) { + return Optional.of(list.stream().map(e -> getBoolean(e).orElseThrow()).toList()); + } + return Optional.empty(); + }); + } + + default Optional getBoolArray(final T input){ + return getStream(input).flatMap(stream -> { + final List list = stream.toList(); + if (list.stream().allMatch(element -> getBoolean(element).isPresent())) { + final boolean[] buf = new boolean[list.size()]; + for (int i = 0; i < list.size(); i++) { + buf[i] = getBoolean(list.get(i)).orElseThrow(); + } + return Optional.of(buf); + } + return Optional.empty(); + }); + } + + default T createBoolList(List value){ + return createList(value.stream().map(this::createBoolean)); + } + + default T createBoolArray(boolean[] value){ + return createList(NumberUtil.boolToStream(value).map(this::createBoolean)); + } + + // byte lists + + default Optional getByteBuffer(final T input) { + return getByteArray(input).map(ByteBuffer::wrap); + } + + default Optional getByteArray(final T input) { + return getStream(input).flatMap(stream -> { + final List list = stream.toList(); + if (list.stream().allMatch(element -> getNumberValue(element).isPresent())) { + final byte[] buf = new byte[list.size()]; + for (int i = 0; i < list.size(); i++) { + buf[i] = getByte(list.get(i)).orElse((byte) 0); + } + return Optional.of(buf); + } + return Optional.empty(); + }); + } + + default Optional> getByteList(final T input){ + return getByteArray(input).map(NumberUtil::byteToList); + } + + default T createByteBuffer(final ByteBuffer input) { + return createList(IntStream.range(0, input.capacity()).mapToObj(i -> createByte(input.get(i)))); + } + + default T createByteArray(final byte[] input) { + return createList(IntStream.range(0, input.length).mapToObj(i -> createByte(input[i]))); + } + + default T createByteList(final List input) { + return createList(input.stream().map(this::createByte)); + } + + // short lists + + default Optional getShortBuffer(final T input) { + return getShortArray(input).map(ShortBuffer::wrap); + } + + default Optional getShortArray(final T input) { + return getStream(input).flatMap(stream -> { + final List list = stream.toList(); + if (list.stream().allMatch(element -> getNumberValue(element).isPresent())) { + final short[] buf = new short[list.size()]; + for (int i = 0; i < list.size(); i++) { + buf[i] = getShort(list.get(i)).orElse((short) 0); + } + return Optional.of(buf); + } + return Optional.empty(); + }); + } + + default Optional> getShortList(final T input){ + return getShortArray(input).map(NumberUtil::shortToList); + } + + default T createShortBuffer(final ShortBuffer input) { + return createList(IntStream.range(0, input.capacity()).mapToObj(i -> createShort(input.get(i)))); + } + + default T createShortArray(final short[] input) { + return createList(IntStream.range(0, input.length).mapToObj(i -> createShort(input[i]))); + } + + default T createShortList(final List input) { + return createList(input.stream().map(this::createShort)); + } + + // int lists + + default Optional getIntStream(final T input) { + return getIntArray(input).map(IntStream::of); + } + + default Optional getIntArray(final T input) { + return getStream(input).flatMap(stream -> { + final List list = stream.toList(); + if (list.stream().allMatch(element -> getNumberValue(element).isPresent())) { + return Optional.of(list.stream().mapToInt(element -> getInt(element).orElseThrow()).toArray()); + } + return Optional.empty(); + }); + } + + default Optional> getIntList(final T input) { + return getIntArray(input).map(NumberUtil::intToList); + } + + default T createIntStream(final IntStream input) { + return createList(input.mapToObj(this::createInt)); + } + + default T createIntArray(final int[] input) { + return createIntStream(Arrays.stream(input)); + } + + default T createIntList(final List input) { + return createList(input.stream().map(this::createInt)); + } + + // long list + + default Optional getLongStream(final T input) { + return getLongArray(input).map(LongStream::of); + } + + default Optional getLongArray(final T input) { + return getStream(input).flatMap(stream -> { + final List list = stream.toList(); + if (list.stream().allMatch(element -> getNumberValue(element).isPresent())) { + return Optional.of(list.stream().mapToLong(element -> getLong(element).orElseThrow()).toArray()); + } + return Optional.empty(); + }); + } + + default Optional> getLongList(final T input) { + return getLongStream(input).map(LongStream::boxed).map(Stream::toList); + } + + default T createLongStream(final LongStream input) { + return createList(input.mapToObj(this::createLong)); + } + + default T createLongList(final List input) { + return createList(input.stream().map(this::createLong)); + } + + default T createLongArray(final long[] input) { + return createLongStream(LongStream.of(input)); + } + + // float list + + default Optional getFloatBuffer(final T input) { + return getStream(input).flatMap(stream -> { + final List list = stream.toList(); + if (list.stream().allMatch(element -> getNumberValue(element).isPresent())) { + final var buf = FloatBuffer.wrap(new float[list.size()]); + for (T t : list) { + buf.put(getFloat(t).orElse((0f))); + } + return Optional.of(buf); + } + return Optional.empty(); + }); + } + + default Optional getFloatArray(final T input) { + return getFloatBuffer(input).map(FloatBuffer::array); + } + + default Optional> getFloatList(final T input) { + return getFloatArray(input).map(NumberUtil::floatToList); + } + + default T createFloatBuffer(final FloatBuffer input) { + return createFloatArray(input.array()); + } + + default T createFloatList(final List input) { + return createList(input.stream().map(this::createFloat)); + } + + default T createFloatArray(final float[] input) { + return createFloatList(NumberUtil.floatToList(input)); + } + + // double lists + + default Optional getDoubleBuffer(final T input) { + return getStream(input).flatMap(stream -> { + final List list = stream.toList(); + if (list.stream().allMatch(element -> getNumberValue(element).isPresent())) { + final var buf = DoubleBuffer.wrap(new double[list.size()]); + for (T t : list) { + buf.put(getDouble(t).orElse(0d)); + } + return Optional.of(buf); + } + return Optional.empty(); + }); + } + + default Optional getDoubleArray(final T input) { + return getDoubleBuffer(input).map(DoubleBuffer::array); + } + + default Optional> getDoubleList(final T input) { + return getDoubleArray(input).map(NumberUtil::doubleToList); + } + + default T createDoubleBuffer(final DoubleBuffer input) { + return createDoubleArray(input.array()); + } + + default T createDoubleList(final List input) { + return createList(input.stream().map(this::createDouble)); + } + + default T createDoubleArray(final double[] input) { + return createDoubleList(NumberUtil.doubleToList(input)); + } + + // end + + T remove(T input, String key); + + default Optional get(final T input, final String key) { + return getGeneric(input, createString(key)); + } + + default Optional getGeneric(final T input, final T key) { + return getMap(input).flatMap(map -> Optional.ofNullable(map.get(key))); + } + + default T set(final T input, final String key, final T value) { + return mergeToMap(input, createString(key), value).orElse(input); + } + + default T update(final T input, final String key, final Function function) { + return get(input, key).map(value -> set(input, key, function.apply(value))).orElse(input); + } + + default T updateGeneric(final T input, final T key, final Function function) { + return getGeneric(input, key).flatMap(value -> mergeToMap(input, key, function.apply(value))).orElse(input); + } + + default U convertList(final TypeOps outOps, final T input) { + return outOps.createList(getStream(input).orElse(Stream.empty()).map(e -> convertTo(outOps, e))); + } + + default U convertMap(final TypeOps outOps, final T input) { + return outOps.createMap(getMapValues(input).orElse(Stream.empty()).map(e -> + Pair.of(convertTo(outOps, e.getFirst()), convertTo(outOps, e.getSecond())) + )); + } +} diff --git a/src/test/java/map/MapTest.java b/core/src/test/java/map/MapTest.java similarity index 89% rename from src/test/java/map/MapTest.java rename to core/src/test/java/map/MapTest.java index 764a452..64f5a73 100644 --- a/src/test/java/map/MapTest.java +++ b/core/src/test/java/map/MapTest.java @@ -63,17 +63,17 @@ public void testSectionList(){ public void testIntList(){ var map = new NestedKeyMap(); map.put("l", new int[]{3, 3, 3}); - System.out.println(map.getIntList("l", null)); + System.out.println(Arrays.toString(map.getIntArray("l", null))); } @Test public void testNums(){ var map = new NestedKeyMap(); - map.put("s", (short) 1); - map.put("i", 2); - map.put("f", 3f); - map.put("d", 4d); - map.put("l", 5L); + map.putValue("s", (short) 1); + map.putValue("i", 2); + map.putValue("f", 3f); + map.putValue("d", 4d); + map.putValue("l", 5L); var d = map.getDouble("d"); assert d == 4.0d; System.out.println("map = " + map); diff --git a/indigodataio-gson/pom.xml b/indigodataio-gson/pom.xml index 6f02122..9c37463 100644 --- a/indigodataio-gson/pom.xml +++ b/indigodataio-gson/pom.xml @@ -4,9 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - xyz.bobkinn + + xyz.bobkinn + indigodataio-parent + 3.0.0 + + indigodataio-gson - 1.0.0 + 2.0.0 17 @@ -18,7 +23,7 @@ xyz.bobkinn indigodataio - 2.0.0 + 3.0.0 com.google.code.gson @@ -40,67 +45,4 @@ - - - github - GitHub Packages - https://maven.pkg.github.com/BoBkiNN/IndigoDataIo - - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - package - - shade - - - false - - - - - - org.apache.maven.plugins - maven-deploy-plugin - 3.1.1 - - - default-deploy - install - - deploy - - - - - - org.apache.maven.plugins - maven-source-plugin - 3.3.0 - - - attach-sources - verify - - jar-no-fork - - - - - - - - src/main/resources - true - - - - \ No newline at end of file diff --git a/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/GsonData.java b/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/GsonData.java index 60ae58a..1d32139 100644 --- a/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/GsonData.java +++ b/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/GsonData.java @@ -8,10 +8,10 @@ import xyz.bobkinn.indigodataio.DataHolder; import xyz.bobkinn.indigodataio.MapBuilder; import xyz.bobkinn.indigodataio.Pair; +import xyz.bobkinn.indigodataio.ops.TypeOps; import java.util.*; import java.util.function.Function; -import java.util.function.Predicate; @RequiredArgsConstructor public class GsonData implements DataHolder { @@ -31,6 +31,11 @@ public GsonData getNew() { return new GsonData(); } + @Override + public TypeOps getOps() { + return GsonOps.INSTANCE; + } + @Override public MapBuilder toBuilder() { return new MapBuilder<>(null, this, GsonData::new, @@ -194,47 +199,6 @@ public static JsonArray mapArray(List source, Function co return arr; } - public A mapPrimitive(String key, Function conv, Predicate typeCheck, A def){ - var p = getType(key, JsonPrimitive.class); - if (p == null) return def; - else if (typeCheck.test(p)) return conv.apply(p); - else return null; - } - - public List mapPrimitiveList(String key, Function conv, List def){ - var arr = getType(key, JsonArray.class); - if (arr == null) return def; - var ls = new ArrayList(arr.size()); - var asList = arr.asList(); - for (var e : asList) { - if (e instanceof JsonPrimitive p) { - try { - ls.add(conv.apply(p)); - } catch (Exception ex) { - return def; - } - } else if (e.isJsonNull()) { - ls.add(null); - } else { - return def; - } - } - return ls; - } - - public List mapNumberList(String key, Function conv, List def){ - var ls = mapPrimitiveList(key, JsonPrimitive::getAsNumber, null); - if (ls == null) return def; - List list = new ArrayList<>(); - for (Number l : ls) { - if (l != null) { - A a = conv.apply(l); - list.add(a); - } else list.add(null); - } - return list; - } - public static JsonPrimitive mapToPrimitive(Object value){ if (value == null) return null; if (value instanceof Boolean b) { @@ -262,7 +226,7 @@ public List getList(String key, List value) { - if (value == null) return put(key, null); + if (value == null) return putValue(key, null); var arr = new JsonArray(value.size()); value.forEach(arr::add); - return put(key, arr); + return putValue(key, arr); } public JsonObject getObject(String key, JsonObject def){ @@ -307,12 +271,12 @@ public List getSectionList(String key, List def) { @Override public JsonElement putSection(String key, GsonData value) { - return put(key, value.data); + return putValue(key, value.data); } @Override public JsonElement putSectionList(String key, List value) { - return put(key, mapArray(value, GsonData::getRaw)); + return putValue(key, mapArray(value, GsonData::getRaw)); } @Override @@ -331,205 +295,21 @@ public Map getMap(String key) { public List> getMapList(String key, List> def) { var arr = getType(key, JsonArray.class); if (arr == null) return def; - List> ls = new ArrayList<>(arr.size()); - arr.asList().forEach(e -> ls.add(e.getAsJsonObject().asMap())); - return ls; + var a = arr.asList(); + if (a.stream().allMatch(JsonElement::isJsonObject)){ + return a.stream().map(JsonElement::getAsJsonObject).map(JsonObject::asMap).toList(); + } + return def; } @Override public JsonElement putMap(String key, Map value) { - return put(key, mapAsObject(value)); + return putValue(key, mapAsObject(value)); } @Override public JsonElement putMapList(String key, List> value) { - return put(key, mapArray(value, GsonData::mapAsObject)); - } - - @Override - public String getString(String key, String def) { - return mapPrimitive(key, JsonPrimitive::getAsString, JsonPrimitive::isString, def); - } - - @Override - public String getString(String key) { - return getString(key, null); - } - - @Override - public List getStringList(String key, List def) { - return mapPrimitiveList(key, JsonPrimitive::getAsString, def); - } - - @Override - public JsonElement putString(String key, String value) { - return put(key, new JsonPrimitive(value)); + return putValue(key, mapArray(value, GsonData::mapAsObject)); } - @Override - public JsonElement putStringList(String key, List value) { - return put(key, mapArray(value, GsonData::mapToPrimitive)); - } - - @Override - public byte getByte(String key, byte def) { - var n = mapPrimitive(key, JsonPrimitive::getAsNumber, JsonPrimitive::isNumber, null); - if (n == null) return def; - return n.byteValue(); - } - - @Override - public byte getByte(String key) { - return getByte(key, (byte) 0); - } - - @Override - public List getByteList(String key, List def) { - return mapNumberList(key, Number::byteValue, def); - } - - @Override - public JsonElement putByte(String key, byte value) { - return put(key, new JsonPrimitive(value)); - } - - @Override - public JsonElement putByteList(String key, List value) { - return put(key, mapArray(value, GsonData::mapToPrimitive)); - } - - @Override - public short getShort(String key, short def) { - var n = mapPrimitive(key, JsonPrimitive::getAsNumber, JsonPrimitive::isNumber, null); - if (n == null) return def; - return n.shortValue(); - } - - @Override - public short getShort(String key) { - return getShort(key, (short) 0); - } - - @Override - public List getShortList(String key, List def) { - return mapNumberList(key, Number::shortValue, def); - } - - @Override - public JsonElement putShort(String key, short value) { - return put(key, new JsonPrimitive(value)); - } - - @Override - public JsonElement putShortList(String key, List value) { - return put(key, mapArray(value, GsonData::mapToPrimitive)); - } - - @Override - public int getInt(String key, int def) { - var n = mapPrimitive(key, JsonPrimitive::getAsNumber, JsonPrimitive::isNumber, null); - if (n == null) return def; - return n.intValue(); - } - - @Override - public int getInt(String key) { - return getInt(key, 0); - } - - @Override - public List getIntList(String key, List def) { - return mapNumberList(key, Number::intValue, def); - } - - @Override - public JsonElement putInt(String key, int value) { - return put(key, new JsonPrimitive(value)); - } - - @Override - public JsonElement putIntList(String key, List value) { - return put(key, mapArray(value, GsonData::mapToPrimitive)); - } - - @Override - public long getLong(String key, long def) { - var n = mapPrimitive(key, JsonPrimitive::getAsNumber, JsonPrimitive::isNumber, null); - if (n == null) return def; - return n.longValue(); - } - - @Override - public long getLong(String key) { - return getLong(key, 0L); - } - - @Override - public List getLongList(String key, List def) { - return mapNumberList(key, Number::longValue, def); - } - - @Override - public JsonElement putLong(String key, long value) { - return put(key, new JsonPrimitive(value)); - } - - @Override - public JsonElement putLongList(String key, List value) { - return put(key, mapArray(value, GsonData::mapToPrimitive)); - } - - @Override - public float getFloat(String key, float def) { - var n = mapPrimitive(key, JsonPrimitive::getAsNumber, JsonPrimitive::isNumber, null); - if (n == null) return def; - return n.floatValue(); - } - - @Override - public float getFloat(String key) { - return getFloat(key, 0); - } - - @Override - public List getFloatList(String key, List def) { - return mapNumberList(key, Number::floatValue, def); - } - - @Override - public JsonElement putFloat(String key, float value) { - return put(key, new JsonPrimitive(value)); - } - - @Override - public JsonElement putFloatList(String key, List value) { - return put(key, mapArray(value, GsonData::mapToPrimitive)); - } - - @Override - public double getDouble(String key, double def) { - var n = mapPrimitive(key, JsonPrimitive::getAsNumber, JsonPrimitive::isNumber, null); - if (n == null) return def; - return n.doubleValue(); - } - - @Override - public double getDouble(String key) { - return getDouble(key, 0); - } - - @Override - public List getDoubleList(String key, List def) { - return mapNumberList(key, Number::doubleValue, def); - } - - @Override - public JsonElement putDouble(String key, double value) { - return put(key, new JsonPrimitive(value)); - } - - @Override - public JsonElement putDoubleList(String key, List value) { - return put(key, mapArray(value, GsonData::mapToPrimitive)); - } } diff --git a/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/GsonOps.java b/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/GsonOps.java new file mode 100644 index 0000000..08f2cae --- /dev/null +++ b/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/GsonOps.java @@ -0,0 +1,203 @@ +package xyz.bobkinn.indigodataio.gson; + +import com.google.gson.*; +import org.jetbrains.annotations.Nullable; +import xyz.bobkinn.indigodataio.Pair; +import xyz.bobkinn.indigodataio.ops.BaseMap; +import xyz.bobkinn.indigodataio.ops.TypeOps; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class GsonOps implements TypeOps { + public static final GsonOps INSTANCE = new GsonOps(); + + @Override + public JsonElement empty() { + return JsonNull.INSTANCE; + } + + @Override + public U convertTo(TypeOps outOps, JsonElement input) { + if (input instanceof JsonObject) return convertMap(outOps, input); + if (input instanceof JsonArray) return convertList(outOps, input); + if (input instanceof JsonNull) return outOps.empty(); + final JsonPrimitive primitive = input.getAsJsonPrimitive(); + if (primitive.isString()) return outOps.createString(primitive.getAsString()); + if (primitive.isBoolean()) return outOps.createBoolean(primitive.getAsBoolean()); + final BigDecimal value = primitive.getAsBigDecimal(); + try { + final long l = value.longValueExact(); + if ((byte) l == l) { + return outOps.createByte((byte) l); + } + if ((short) l == l) { + return outOps.createShort((short) l); + } + if ((int) l == l) { + return outOps.createInt((int) l); + } + return outOps.createLong(l); + } catch (final ArithmeticException e) { + final double d = value.doubleValue(); + if ((float) d == d) { + return outOps.createFloat((float) d); + } + return outOps.createDouble(d); + } + } + + @Override + public Optional getNumberValue(JsonElement input) { + if (input instanceof JsonPrimitive p && p.isNumber()) { + return Optional.of(p.getAsNumber()); + } + return Optional.empty(); + } + + @Override + public JsonElement createNumeric(Number i) { + return new JsonPrimitive(i); + } + + @Override + public JsonElement createBoolean(boolean value) { + return new JsonPrimitive(value); + } + + @Override + public Optional getString(JsonElement input) { + if (input instanceof JsonPrimitive p && p.isString()) { + return Optional.of(p.getAsString()); + } + return Optional.empty(); + } + + @Override + public JsonElement createString(String value) { + return new JsonPrimitive(value); + } + + @Override + public Optional mergeToList(JsonElement list, JsonElement value) { + if (!(list instanceof JsonArray) && list != empty()) { + return Optional.empty(); + } + + final JsonArray result = new JsonArray(); + if (list != empty()) { + result.addAll(list.getAsJsonArray()); + } + result.add(value); + return Optional.of(result); + } + + @Override + public Optional mergeToMap(JsonElement map, JsonElement key, JsonElement value) { + if (!(map instanceof JsonObject) && map != empty()) { + return Optional.empty(); + } + if (!(key instanceof JsonPrimitive p) || !p.isString()) { + return Optional.empty(); + } + + final JsonObject output = new JsonObject(); + if (map != empty()) { + map.getAsJsonObject().entrySet().forEach(entry -> output.add(entry.getKey(), entry.getValue())); + } + output.add(key.getAsString(), value); + + return Optional.of(output); + } + + @Override + public Optional>> getMapValues(JsonElement input) { + if (!(input instanceof JsonObject o)) { + return Optional.empty(); + } + return Optional.of(o.entrySet().stream().map(entry -> + Pair.of(new JsonPrimitive(entry.getKey()), entry.getValue() instanceof JsonNull ? null : entry.getValue()))); + } + + @Override + public Optional> getMap(JsonElement input) { + if (!(input instanceof JsonObject object)) { + return Optional.empty(); + } + return Optional.of(new BaseMap<>() { + @Nullable + @Override + public JsonElement get(final JsonElement key) { + final JsonElement element = object.get(key.getAsString()); + if (element instanceof JsonNull) { + return null; + } + return element; + } + + @Nullable + @Override + public JsonElement get(final String key) { + final JsonElement element = object.get(key); + if (element instanceof JsonNull) { + return null; + } + return element; + } + + @Override + public Stream> entries() { + return object.entrySet().stream().map(e -> Pair.of(new JsonPrimitive(e.getKey()), e.getValue())); + } + + @Override + public String toString() { + return "BaseMap[" + object + "]"; + } + }); + } + + @Override + public JsonElement createMap(Stream> map) { + final JsonObject result = new JsonObject(); + map.forEach(p -> result.add(p.getFirst().getAsString(), p.getSecond())); + return result; + } + + @Override + public Optional> getStream(JsonElement input) { + if (input instanceof JsonArray arr) { + return Optional.of(StreamSupport.stream(arr.spliterator(), false).map(e -> e instanceof JsonNull ? null : e)); + } + return Optional.empty(); + } + + @Override + public JsonElement createList(Stream input) { + var list = input.map(v -> v == null ? empty() : v).toList(); + final JsonArray ret = new JsonArray(list.size()); + list.forEach(ret::add); + return ret; + } + + @Override + public JsonElement createArray(JsonElement[] input) { + return createList(Arrays.stream(input)); + } + + @Override + public JsonElement remove(JsonElement input, String key) { + if (input instanceof JsonObject) { + final JsonObject result = new JsonObject(); + input.getAsJsonObject().entrySet().stream() + .filter(entry -> !Objects.equals(entry.getKey(), key)) + .forEach(entry -> result.add(entry.getKey(), entry.getValue())); + return result; + } + return input; + } +} diff --git a/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/io/JsonIo.java b/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/io/JsonIo.java new file mode 100644 index 0000000..4664bb6 --- /dev/null +++ b/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/io/JsonIo.java @@ -0,0 +1,71 @@ +package xyz.bobkinn.indigodataio.gson.io; + +import com.google.gson.*; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import xyz.bobkinn.indigodataio.gson.GsonData; + +import java.io.*; +import java.lang.reflect.Type; + +@SuppressWarnings("unused") +public class JsonIo { + public static final GsonDataAdapter ADAPTER = new GsonDataAdapter(); + public static final WriterOptions DEFAULT_OPTIONS = WriterOptions.NORMAL; + + public static class GsonDataAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public JsonElement serialize(GsonData src, Type typeOfSrc, JsonSerializationContext context) { + return src.getRaw(); + } + + @Override + public GsonData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx) throws JsonParseException { + if (json instanceof JsonObject o){ + return new GsonData(o); + } + throw new JsonParseException("Json elements is not object"); + } + } + + public static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(GsonData.class, ADAPTER) + .create(); + + private static void createParentFolder(File folder){ + if (!folder.isDirectory()) if (!folder.mkdirs()) throw new IllegalStateException("Failed to create folder "+folder); + } + + public static void write(GsonData data, File to, WriterOptions options){ + createParentFolder(to); + try (var wr = new JsonWriter(new BufferedWriter(new FileWriter(to, options.getCharset())))) { + options.apply(wr); + GSON.toJson(data, GsonData.class, wr); + } catch (JsonIOException e){ + throw new RuntimeException("Failed to write json to "+to, e); + } catch (IOException e){ + throw new RuntimeException("Failed to write file "+to, e); + } + } + + public static void write(GsonData data, File to){ + write(data, to, DEFAULT_OPTIONS); + } + + public static GsonData read(File from, WriterOptions options){ + if (!from.isFile()) throw new IllegalArgumentException("File "+from+" does not exists or is directory"); + try (var r = new JsonReader(new BufferedReader(new FileReader(from, options.getCharset())))) { + return GSON.fromJson(r, GsonData.class); + } catch (JsonIOException e){ + throw new RuntimeException("Failed to read json to "+from, e); + } catch (IOException e){ + throw new RuntimeException("Failed to read file "+from, e); + } + } + + public static GsonData read(File from){ + return read(from, DEFAULT_OPTIONS); + } + +} diff --git a/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/io/WriterOptions.java b/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/io/WriterOptions.java new file mode 100644 index 0000000..8c4f933 --- /dev/null +++ b/indigodataio-gson/src/main/java/xyz/bobkinn/indigodataio/gson/io/WriterOptions.java @@ -0,0 +1,53 @@ +package xyz.bobkinn.indigodataio.gson.io; + +import com.google.gson.GsonBuilder; +import com.google.gson.stream.JsonWriter; +import lombok.Getter; +import lombok.Setter; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + + +@SuppressWarnings("LombokSetterMayBeUsed") +@Getter +public class WriterOptions { + public static final WriterOptions MINIMIZED = new WriterOptions(); + /** + * Equals to {@link GsonBuilder#setPrettyPrinting()} + */ + public static final WriterOptions NORMAL = of(2); + public static final WriterOptions TABS = new WriterOptions(); + + public static WriterOptions of(int indent){ + var ret = new WriterOptions(); + ret.setIndent(indent); + return ret; + } + + static { + MINIMIZED.setMinimized(); + TABS.setIndent("\t"); + } + + private String indent = ""; + @Setter + private Charset charset = StandardCharsets.UTF_8; + + public void setMinimized(){ + setIndent(0); + } + + public void setIndent(int indent){ + if (indent <= 0 ) setIndent(""); + else setIndent(" ".repeat(indent)); + } + + public void setIndent(String indent){ + this.indent = indent; + } + + public void apply(JsonWriter w){ + w.setIndent(indent); + } +} diff --git a/indigodataio-gson/src/test/java/xyz/bobkinn/indigodataio/gson/TestGson.java b/indigodataio-gson/src/test/java/xyz/bobkinn/indigodataio/gson/TestGson.java index 97f7245..b42bee7 100644 --- a/indigodataio-gson/src/test/java/xyz/bobkinn/indigodataio/gson/TestGson.java +++ b/indigodataio-gson/src/test/java/xyz/bobkinn/indigodataio/gson/TestGson.java @@ -2,6 +2,7 @@ import com.google.gson.JsonPrimitive; import org.junit.Test; +import xyz.bobkinn.indigodataio.ops.MapOps; import java.util.Arrays; import java.util.List; @@ -58,6 +59,11 @@ public void testSectionList(){ System.out.println(map); var maps2 = map.getMapArray("ls"); System.out.println(Arrays.toString(maps2)); + map.putBoolean("b", true); + + System.out.println("Converting "+map); + var converted = GsonOps.INSTANCE.convertMap(MapOps.INSTANCE, map.getRaw()); + System.out.println("converted = " + converted); } @Test diff --git a/pom.xml b/pom.xml index 9c97f3f..a1ab181 100644 --- a/pom.xml +++ b/pom.xml @@ -5,34 +5,14 @@ 4.0.0 xyz.bobkinn - indigodataio - 2.0.0 + indigodataio-parent + 3.0.0 + pom - - 17 - 17 - UTF-8 - - - - - org.jetbrains - annotations - 24.0.1 - - - org.projectlombok - lombok - 1.18.26 - provided - - - junit - junit - 4.10 - test - - + + core + indigodataio-gson + @@ -67,7 +47,7 @@ default-deploy - install + deploy deploy diff --git a/src/main/java/xyz/bobkinn/indigodataio/DataHolder.java b/src/main/java/xyz/bobkinn/indigodataio/DataHolder.java deleted file mode 100644 index c0f348e..0000000 --- a/src/main/java/xyz/bobkinn/indigodataio/DataHolder.java +++ /dev/null @@ -1,408 +0,0 @@ -package xyz.bobkinn.indigodataio; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Main interface for data holders.
- * Data types:
- *
    - *
  • {@link T} section
  • - *
  • {@link P} basic type
  • - *
  • {@link Map} {@code Map} map
  • - *
  • {@link String}
  • - *
  • {@link Byte}
  • - *
  • {@link Short}
  • - *
  • {@link Integer}
  • - *
  • {@link Long}
  • - *
  • {@link Float}
  • - *
  • {@link Double}
  • - *
- * And all of this has {@link List} and array getters and setter too - * @param implementation type - * @param

parent class type, like Object from {@link NestedKeyMap}, JsonElement from GSON or Tag from NBT.
- * Meant to be internal type that used as value in internal object - */ -@SuppressWarnings({"unused", "UnusedReturnValue"}) -public interface DataHolder, P> { - - @SafeVarargs - static List toList(T... array){ - if (array == null) return null; - return List.of(array); - } - - @SuppressWarnings("unchecked") - static T[] toArray(List c){ - if (c == null) return null; - return (T[]) c.toArray(); - } - - T getNew(); - - /** - * @return new builder - */ - MapBuilder toBuilder(); - - /** - * Get a builder view on this key - * @param key key - * @return new builder - */ - MapBuilder toBuilder(String key); - - P remove(String key); - - boolean contains(String key); - - boolean contains(String key, Class type); - - boolean containsSection(String key); - - Set keys(); - - Set keys(String key); - - int size(); - - void clear(); - - default List getDefaultList(){ - return null; - } - - /* BASIC METHODS */ - - // getters - - P get(String key, P def); - - P get(String key); - - List getList(String key, List def); - - default List getList(String key) { - return getList(key, getDefaultList()); - } - - default P[] getArray(String key, P[] def) { - return toArray(getList(key, toList(def))); - } - - default P[] getArray(String key){ - return toArray(getList(key)); - } - - // setters - - P put(String key, P value); - - P putList(String key, List value); - - default P putArray(String key, P[] value) { - return putList(key, toList(value)); - } - - /* SECTION METHODS */ - - // getters - - T getSection(String key, T def); - - T getSection(String key); - - List getSectionList(String key, List def); - - default List getSectionList(String key){ - return getSectionList(key, getDefaultList()); - } - - default T[] getSectionArray(String key, T[] def){ - return toArray(getSectionList(key, toList(def))); - } - - default T[] getSectionArray(String key){ - return toArray(getSectionList(key)); - } - - // setters - - P putSection(String key, T value); - - P putSectionList(String key, List value); - - default P putSectionArray(String key, T[] value){ - return putSectionList(key, toList(value)); - } - - /* MAP METHODS */ - - // getters - - Map getMap(String key, Map def); - - Map getMap(String key); - - List> getMapList(String key, List> def); - - default List> getMapList(String key){ - return getMapList(key, getDefaultList()); - } - - default Map[] getMapArray(String key, Map[] def) { - //noinspection unchecked - return (Map[]) getMapList(key, toList(def)).toArray(Map[]::new); - } - - default Map[] getMapArray(String key){ - //noinspection unchecked - return (Map[]) getMapList(key).toArray(Map[]::new); - } - - // setters - - P putMap(String key, Map value); - - P putMapList(String key, List> value); - - default P putMapArray(String key, Map[] value) { - return putMapList(key, toList(value)); - } - - // - - /* String methods */ - - // getters - - String getString(String key, String def); - - String getString(String key); - - List getStringList(String key, List def); - - default List getStringList(String key){ - return getStringList(key, getDefaultList()); - } - - default String[] getStringArray(String key, String[] def){ - return toArray(getStringList(key, toList(def))); - } - - default String[] getStringArray(String key){ - return toArray(getStringList(key)); - } - - // setters - - P putString(String key, String value); - - P putStringList(String key, List value); - - default P putStringArray(String key, String[] value){ - return putStringList(key, toList(value)); - } - - - /* Byte methods */ - - // getters - - byte getByte(String key, byte def); - - byte getByte(String key); - - List getByteList(String key, List def); - - default List getByteList(String key){ - return getByteList(key, getDefaultList()); - } - - default byte[] getByteArray(String key, byte[] def){ - return NumberUtil.listToByte(getByteList(key, NumberUtil.byteToList(def))); - } - - default byte[] getByteArray(String key){ - return NumberUtil.listToByte(getByteList(key)); - } - - // setters - - P putByte(String key, byte value); - - P putByteList(String key, List value); - - default P putByteArray(String key, byte[] value){ - return putByteList(key, NumberUtil.byteToList(value)); - } - - - /* Short methods */ - - // getters - - short getShort(String key, short def); - - short getShort(String key); - - List getShortList(String key, List def); - - default List getShortList(String key){ - return getShortList(key, getDefaultList()); - } - - default short[] getShortArray(String key, short[] def){ - return NumberUtil.listToShort(getShortList(key, NumberUtil.shortToList(def))); - } - - default short[] getShortArray(String key){ - return NumberUtil.listToShort(getShortList(key)); - } - - // setters - - P putShort(String key, short value); - - P putShortList(String key, List value); - - default P putShortArray(String key, short[] value){ - return putShortList(key, NumberUtil.shortToList(value)); - } - - - /* Integer methods */ - - // getters - - int getInt(String key, int def); - - int getInt(String key); - - List getIntList(String key, List def); - - default List getIntList(String key){ - return getIntList(key, getDefaultList()); - } - - default int[] getIntArray(String key, int[] def){ - return NumberUtil.listToInt(getIntList(key, NumberUtil.intToList(def))); - } - - default int[] getIntArray(String key){ - return NumberUtil.listToInt(getIntList(key)); - } - - // setters - - P putInt(String key, int value); - - P putIntList(String key, List value); - - default P putIntArray(String key, int[] value){ - return putIntList(key, NumberUtil.intToList(value)); - } - - - /* Long methods */ - - // getters - - long getLong(String key, long def); - - long getLong(String key); - - List getLongList(String key, List def); - - default List getLongList(String key){ - return getLongList(key, getDefaultList()); - } - - default long[] getLongArray(String key, long[] def){ - return NumberUtil.listToLong(getLongList(key, NumberUtil.longToList(def))); - } - - default long[] getLongArray(String key){ - return NumberUtil.listToLong(getLongList(key)); - } - - // setters - - P putLong(String key, long value); - - P putLongList(String key, List value); - - default P putLongArray(String key, long[] value){ - return putLongList(key, NumberUtil.longToList(value)); - } - - - /* Float methods */ - - // getters - - float getFloat(String key, float def); - - float getFloat(String key); - - List getFloatList(String key, List def); - - default List getFloatList(String key){ - return getFloatList(key, getDefaultList()); - } - - default float[] getFloatArray(String key, float[] def){ - return NumberUtil.listToFloat(getFloatList(key, NumberUtil.floatToList(def))); - } - - default float[] getFloatArray(String key){ - return NumberUtil.listToFloat(getFloatList(key)); - } - - // setters - - P putFloat(String key, float value); - - P putFloatList(String key, List value); - - default P putFloatArray(String key, float[] value){ - return putFloatList(key, NumberUtil.floatToList(value)); - } - - - /* Double methods */ - - // getters - - double getDouble(String key, double def); - - double getDouble(String key); - - List getDoubleList(String key, List def); - - default List getDoubleList(String key){ - return getDoubleList(key, getDefaultList()); - } - - default double[] getDoubleArray(String key, double[] def){ - return NumberUtil.listToDouble(getDoubleList(key, NumberUtil.doubleToList(def))); - } - - default double[] getDoubleArray(String key){ - return NumberUtil.listToDouble(getDoubleList(key)); - } - - // setters - - P putDouble(String key, double value); - - P putDoubleList(String key, List value); - - default P putDoubleArray(String key, double[] value){ - return putDoubleList(key, NumberUtil.doubleToList(value)); - } - - // - -}