diff --git a/build.gradle b/build.gradle index 9c7b9e55..5260b33a 100644 --- a/build.gradle +++ b/build.gradle @@ -39,6 +39,7 @@ dependencies { compile 'com.google.guava:guava:21.0' compile 'org.apache.commons:commons-lang3:3.5' compile 'it.unimi.dsi:fastutil:7.1.0' + testCompile 'junit:junit-dep:4.10' } task sourcesJar(type: Jar) { diff --git a/gradle.properties b/gradle.properties index 66a62f35..a07d0e62 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -majorMinor: 2.0 +majorMinor: 3.0 diff --git a/src/main/java/com/mojang/datafixers/DSL.java b/src/main/java/com/mojang/datafixers/DSL.java index be9d3f51..a1580c1b 100644 --- a/src/main/java/com/mojang/datafixers/DSL.java +++ b/src/main/java/com/mojang/datafixers/DSL.java @@ -3,24 +3,11 @@ package com.mojang.datafixers; import com.google.common.collect.Maps; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; -import com.mojang.datafixers.util.Unit; -import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.schemas.Schema; import com.mojang.datafixers.types.Func; import com.mojang.datafixers.types.Type; -import com.mojang.datafixers.types.constant.BoolType; -import com.mojang.datafixers.types.constant.ByteType; -import com.mojang.datafixers.types.constant.DoubleType; -import com.mojang.datafixers.types.constant.FloatType; -import com.mojang.datafixers.types.constant.IntType; -import com.mojang.datafixers.types.constant.LongType; -import com.mojang.datafixers.types.constant.NamespacedStringType; -import com.mojang.datafixers.types.constant.NilDrop; -import com.mojang.datafixers.types.constant.NilSave; -import com.mojang.datafixers.types.constant.ShortType; -import com.mojang.datafixers.types.constant.StringType; +import com.mojang.datafixers.types.constant.EmptyPart; +import com.mojang.datafixers.types.constant.EmptyPartPassthrough; import com.mojang.datafixers.types.templates.Check; import com.mojang.datafixers.types.templates.CompoundList; import com.mojang.datafixers.types.templates.Const; @@ -33,13 +20,17 @@ import com.mojang.datafixers.types.templates.Tag; import com.mojang.datafixers.types.templates.TaggedChoice; import com.mojang.datafixers.types.templates.TypeTemplate; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.datafixers.util.Unit; +import com.mojang.serialization.Codec; +import com.mojang.serialization.Dynamic; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Triple; import java.util.Map; import java.util.function.Function; import java.util.function.Supplier; -import java.util.stream.Collectors; public interface DSL { interface TypeReference { @@ -84,24 +75,20 @@ static Type string() { return Instances.STRING_TYPE; } - static Type namespacedString() { - return Instances.NAMESPACED_STRING_TYPE; - } - - static TypeTemplate nil() { - return constType(Instances.NIL_DROP); + static TypeTemplate emptyPart() { + return constType(Instances.EMPTY_PART); } - static Type nilType() { - return Instances.NIL_DROP; + static Type emptyPartType() { + return Instances.EMPTY_PART; } static TypeTemplate remainder() { - return constType(Instances.NIL_SAVE); + return constType(Instances.EMPTY_PASSTHROUGH); } static Type> remainderType() { - return Instances.NIL_SAVE; + return Instances.EMPTY_PASSTHROUGH; } static TypeTemplate check(final String name, final int index, final TypeTemplate element) { @@ -208,11 +195,11 @@ static TaggedChoice taggedChoice(final String name, final Type keyType } static TaggedChoice taggedChoiceLazy(final String name, final Type keyType, final Map> templates) { - return taggedChoice(name, keyType, templates.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue().get())).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond))); + return taggedChoice(name, keyType, templates.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue().get())).collect(Pair.toMap())); } @SuppressWarnings("unchecked") - static Type> taggedChoiceType(final String name, final Type keyType, final Map> types) { + static Type> taggedChoiceType(final String name, final Type keyType, final Map> types) { return (Type>) Instances.TAGGED_CHOICE_TYPE_CACHE.computeIfAbsent(Triple.of(name, keyType, types), k -> new TaggedChoice.TaggedChoiceType<>(k.getLeft(), (Type) k.getMiddle(), (Map>) k.getRight())); } @@ -223,11 +210,11 @@ static Type> func(final Type input, final Type outpu // Helpers static Type> optional(final Type type) { - return or(type, nilType()); + return or(type, emptyPartType()); } static TypeTemplate optional(final TypeTemplate value) { - return or(value, nil()); + return or(value, emptyPart()); } static TypeTemplate fields( @@ -443,24 +430,23 @@ static OpticFinder namedChoice(final String name, final Type type) } static Unit unit() { - return null; + return Unit.INSTANCE; } final class Instances { - private static final Type BOOL_TYPE = new BoolType(); - private static final Type INT_TYPE = new IntType(); - private static final Type LONG_TYPE = new LongType(); - private static final Type BYTE_TYPE = new ByteType(); - private static final Type SHORT_TYPE = new ShortType(); - private static final Type FLOAT_TYPE = new FloatType(); - private static final Type DOUBLE_TYPE = new DoubleType(); - private static final Type STRING_TYPE = new StringType(); - private static final Type NAMESPACED_STRING_TYPE = new NamespacedStringType(); - private static final Type NIL_DROP = new NilDrop(); - private static final Type> NIL_SAVE = new NilSave(); + private static final Type BOOL_TYPE = new Const.PrimitiveType<>(Codec.BOOL); + private static final Type INT_TYPE = new Const.PrimitiveType<>(Codec.INT); + private static final Type LONG_TYPE = new Const.PrimitiveType<>(Codec.LONG); + private static final Type BYTE_TYPE = new Const.PrimitiveType<>(Codec.BYTE); + private static final Type SHORT_TYPE = new Const.PrimitiveType<>(Codec.SHORT); + private static final Type FLOAT_TYPE = new Const.PrimitiveType<>(Codec.FLOAT); + private static final Type DOUBLE_TYPE = new Const.PrimitiveType<>(Codec.DOUBLE); + private static final Type STRING_TYPE = new Const.PrimitiveType<>(Codec.STRING); + private static final Type EMPTY_PART = new EmptyPart(); + private static final Type> EMPTY_PASSTHROUGH = new EmptyPartPassthrough(); private static final OpticFinder> REMAINDER_FINDER = remainderType().finder(); - private static final Map, Map>>, Type>> TAGGED_CHOICE_TYPE_CACHE = Maps.newConcurrentMap(); + private static final Map, Map>>, Type>> TAGGED_CHOICE_TYPE_CACHE = Maps.newConcurrentMap(); } } diff --git a/src/main/java/com/mojang/datafixers/DataFix.java b/src/main/java/com/mojang/datafixers/DataFix.java index f396fcbb..c0cc99fc 100644 --- a/src/main/java/com/mojang/datafixers/DataFix.java +++ b/src/main/java/com/mojang/datafixers/DataFix.java @@ -3,14 +3,17 @@ package com.mojang.datafixers; import com.mojang.datafixers.schemas.Schema; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.DynamicOps; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; import java.util.BitSet; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; public abstract class DataFix { @@ -40,9 +43,17 @@ protected TypeRewriteRule writeAndRead(final String name, final Type type, fi } protected TypeRewriteRule writeFixAndRead(final String name, final Type type, final Type newType, final Function, Dynamic> fix) { - return fixTypeEverywhere(name, type, newType, ops -> input -> - newType.readTyped(fix.apply(type.writeDynamic(ops, input))).getSecond().orElseThrow(() -> new IllegalStateException("Could not read new type in \"" + name + "\"")).getValue() - ); + return fixTypeEverywhere(name, type, newType, ops -> input -> { + final Optional> written = type.writeDynamic(ops, input).resultOrPartial(LOGGER::error); + if (!written.isPresent()) { + throw new RuntimeException("Could not write the object in " + name); + } + final Optional, ?>> read = newType.readTyped(fix.apply(written.get())).resultOrPartial(LOGGER::error); + if (!read.isPresent()) { + throw new RuntimeException("Could not read the new object in " + name); + } + return read.get().getFirst().getValue(); + }); } protected TypeRewriteRule fixTypeEverywhere(final String name, final Type type, final Type newType, final Function, Function> function) { diff --git a/src/main/java/com/mojang/datafixers/DataFixUtils.java b/src/main/java/com/mojang/datafixers/DataFixUtils.java index 3a2046a1..cf8959ae 100644 --- a/src/main/java/com/mojang/datafixers/DataFixUtils.java +++ b/src/main/java/com/mojang/datafixers/DataFixUtils.java @@ -6,6 +6,7 @@ import java.util.Optional; import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.function.UnaryOperator; public class DataFixUtils { private DataFixUtils() { @@ -93,4 +94,11 @@ public static int getVersion(final int key) { public static int getSubVersion(final int key) { return key % 10; } + + public static UnaryOperator consumerToFunction(final Consumer consumer) { + return s -> { + consumer.accept(s); + return s; + }; + } } diff --git a/src/main/java/com/mojang/datafixers/DataFixer.java b/src/main/java/com/mojang/datafixers/DataFixer.java index e8b8b2bb..234eaa3b 100644 --- a/src/main/java/com/mojang/datafixers/DataFixer.java +++ b/src/main/java/com/mojang/datafixers/DataFixer.java @@ -3,6 +3,7 @@ package com.mojang.datafixers; import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; public interface DataFixer { Dynamic update(DSL.TypeReference type, Dynamic input, int version, int newVersion); diff --git a/src/main/java/com/mojang/datafixers/DataFixerUpper.java b/src/main/java/com/mojang/datafixers/DataFixerUpper.java index b6eca89e..b8570111 100644 --- a/src/main/java/com/mojang/datafixers/DataFixerUpper.java +++ b/src/main/java/com/mojang/datafixers/DataFixerUpper.java @@ -4,19 +4,20 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import com.mojang.datafixers.functions.PointFreeRule; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.Type; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.Dynamic; import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap; import it.unimi.dsi.fastutil.ints.IntSortedSet; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import com.mojang.datafixers.functions.PointFreeRule; -import com.mojang.datafixers.schemas.Schema; -import com.mojang.datafixers.types.Type; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.List; -import java.util.Optional; /* * Optimizing functions @@ -78,18 +79,11 @@ protected DataFixerUpper(final Int2ObjectSortedMap schemas, final List Dynamic update(final DSL.TypeReference type, final Dynamic input, final int version, final int newVersion) { - try { - if (version < newVersion) { - final Type dataType = getType(type, version); - final Optional read = dataType.readAndWrite(input.getOps(), getType(type, newVersion), getRule(version, newVersion), OPTIMIZATION_RULE, input.getValue()); - if (!read.isPresent()) { - throw new IllegalStateException("Could not parse for fixing " + dataType); - } - - return new Dynamic<>(input.getOps(), read.get()); - } - } catch (final Throwable t) { - LOGGER.error("Something went wrong upgrading!", t); + if (version < newVersion) { + final Type dataType = getType(type, version); + final DataResult read = dataType.readAndWrite(input.getOps(), getType(type, newVersion), getRule(version, newVersion), OPTIMIZATION_RULE, input.getValue()); + final T result = read.resultOrPartial(LOGGER::error).orElse(input.getValue()); + return new Dynamic<>(input.getOps(), result); } return input; } diff --git a/src/main/java/com/mojang/datafixers/Dynamic.java b/src/main/java/com/mojang/datafixers/Dynamic.java deleted file mode 100644 index e114b07e..00000000 --- a/src/main/java/com/mojang/datafixers/Dynamic.java +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers; - -import com.google.common.collect.ImmutableMap; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.Type; -import com.mojang.datafixers.util.Pair; - -import javax.annotation.Nullable; -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.LongStream; -import java.util.stream.Stream; - -@SuppressWarnings("unused") -public class Dynamic extends DynamicLike { - private final T value; - - public Dynamic(final DynamicOps ops) { - this(ops, ops.empty()); - } - - public Dynamic(final DynamicOps ops, @Nullable final T value) { - super(ops); - this.value = value == null ? ops.empty() : value; - } - - public T getValue() { - return value; - } - - public Dynamic map(final Function function) { - return new Dynamic<>(ops, function.apply(value)); - } - - @SuppressWarnings("unchecked") - public Dynamic castTyped(final DynamicOps ops) { - if (!Objects.equals(this.ops, ops)) { - throw new IllegalStateException("Dynamic type doesn't match"); - } - return (Dynamic) this; - } - - public U cast(final DynamicOps ops) { - return castTyped(ops).getValue(); - } - - public Dynamic merge(final Dynamic value) { - return map(v -> ops.mergeInto(v, value.cast(ops))); - } - - public Dynamic merge(final Dynamic key, final Dynamic value) { - return map(v -> ops.mergeInto(v, key.cast(ops), value.cast(ops))); - } - - public Optional, Dynamic>> getMapValues() { - return ops.getMapValues(value).map(map -> { - final ImmutableMap.Builder, Dynamic> builder = ImmutableMap.builder(); - for (final Map.Entry entry : map.entrySet()) { - builder.put(new Dynamic<>(ops, entry.getKey()), new Dynamic<>(ops, entry.getValue())); - } - return builder.build(); - }); - } - - public Dynamic updateMapValues(final Function, Dynamic>, Pair, Dynamic>> updater) { - return DataFixUtils.orElse(getMapValues().map(map -> map.entrySet().stream().map(e -> { - final Pair, Dynamic> pair = updater.apply(Pair.of(e.getKey(), e.getValue())); - return Pair.of(pair.getFirst().castTyped(ops), pair.getSecond().castTyped(ops)); - }).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond))).map(this::createMap), this); - } - - @Override - public Optional asNumber() { - return ops.getNumberValue(value); - } - - @Override - public Optional asString() { - return ops.getStringValue(value); - } - - @Override - public Optional>> asStreamOpt() { - return ops.getStream(value).map(s -> s.map(e -> new Dynamic<>(ops, e))); - } - - @Override - public Optional asByteBufferOpt() { - return ops.getByteBuffer(value); - } - - @Override - public Optional asIntStreamOpt() { - return ops.getIntStream(value); - } - - @Override - public Optional asLongStreamOpt() { - return ops.getLongStream(value); - } - - @Override - public OptionalDynamic get(final String key) { - return new OptionalDynamic<>(ops, ops.get(value, key).map(v -> new Dynamic<>(ops, v))); - } - - @Override - public Optional getGeneric(final T key) { - return ops.getGeneric(value, key); - } - - public Dynamic remove(final String key) { - return map(v -> ops.remove(v, key)); - } - - public Dynamic set(final String key, final Dynamic value) { - return map(v -> ops.set(v, key, value.cast(ops))); - } - - public Dynamic update(final String key, final Function, Dynamic> function) { - return map(v -> ops.update(v, key, value -> function.apply(new Dynamic<>(ops, value)).cast(ops))); - } - - public Dynamic updateGeneric(final T key, final Function function) { - return map(v -> ops.updateGeneric(v, key, function)); - } - - @Override - public Optional getElement(final String key) { - return getElementGeneric(ops.createString(key)); - } - - @Override - public Optional getElementGeneric(final T key) { - return ops.getMapValues(value).flatMap(m -> Optional.ofNullable(m.get(key))); - } - - @Override - public Optional> asListOpt(final Function, U> deserializer) { - return asStreamOpt().map(stream -> stream.map(deserializer).collect(Collectors.toList())); - } - - @Override - public Optional> asMapOpt(final Function, K> keyDeserializer, final Function, V> valueDeserializer) { - return ops.getMapValues(value).map(map -> { - final ImmutableMap.Builder builder = ImmutableMap.builder(); - for (final Map.Entry entry : map.entrySet()) { - builder.put(keyDeserializer.apply(new Dynamic<>(ops, entry.getKey())), valueDeserializer.apply(new Dynamic<>(ops, entry.getValue()))); - } - return builder.build(); - }); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final Dynamic dynamic = (Dynamic) o; - return Objects.equals(ops, dynamic.ops) && Objects.equals(value, dynamic.value); - } - - @Override - public int hashCode() { - return Objects.hash(ops, value); - } - - @Override - public String toString() { - return String.format("%s[%s]", ops, value); - } - - public Dynamic convert(final DynamicOps outOps) { - return new Dynamic<>(outOps, convert(ops, outOps, value)); - } - - @SuppressWarnings("unchecked") - public static T convert(final DynamicOps inOps, final DynamicOps outOps, final S input) { - if (Objects.equals(inOps, outOps)) { - return (T) input; - } - final Type type = inOps.getType(input); - if (Objects.equals(type, DSL.nilType())) { - return outOps.empty(); - } - if (Objects.equals(type, DSL.byteType())) { - return outOps.createByte(inOps.getNumberValue(input, 0).byteValue()); - } - if (Objects.equals(type, DSL.shortType())) { - return outOps.createShort(inOps.getNumberValue(input, 0).shortValue()); - } - if (Objects.equals(type, DSL.intType())) { - return outOps.createInt(inOps.getNumberValue(input, 0).intValue()); - } - if (Objects.equals(type, DSL.longType())) { - return outOps.createLong(inOps.getNumberValue(input, 0).longValue()); - } - if (Objects.equals(type, DSL.floatType())) { - return outOps.createFloat(inOps.getNumberValue(input, 0).floatValue()); - } - if (Objects.equals(type, DSL.doubleType())) { - return outOps.createDouble(inOps.getNumberValue(input, 0).doubleValue()); - } - if (Objects.equals(type, DSL.bool())) { - return outOps.createBoolean(inOps.getBooleanValue(input).orElse(false)); - } - if (Objects.equals(type, DSL.string())) { - return outOps.createString(inOps.getStringValue(input).orElse("")); - } - if (Objects.equals(type, DSL.list(DSL.byteType()))) { - return outOps.createByteList(inOps.getByteBuffer(input).orElse(ByteBuffer.wrap(new byte[0]))); - } - if (Objects.equals(type, DSL.list(DSL.intType()))) { - return outOps.createIntList(inOps.getIntStream(input).orElse(IntStream.empty())); - } - if (Objects.equals(type, DSL.list(DSL.longType()))) { - return outOps.createLongList(inOps.getLongStream(input).orElse(LongStream.empty())); - } - if (Objects.equals(type, DSL.list(DSL.remainderType()))) { - return outOps.createList(inOps.getStream(input).orElse(Stream.empty()).map(e -> convert(inOps, outOps, e))); - } - if (Objects.equals(type, DSL.compoundList(DSL.remainderType(), DSL.remainderType()))) { - return outOps.createMap(inOps.getMapValues(input).orElse(ImmutableMap.of()).entrySet().stream().map(e -> - Pair.of(convert(inOps, outOps, e.getKey()), convert(inOps, outOps, e.getValue())) - ).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond))); - } - throw new IllegalStateException("Could not convert value of type " + type); - } -} diff --git a/src/main/java/com/mojang/datafixers/DynamicLike.java b/src/main/java/com/mojang/datafixers/DynamicLike.java deleted file mode 100644 index 9fb0fc4d..00000000 --- a/src/main/java/com/mojang/datafixers/DynamicLike.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.mojang.datafixers; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.mojang.datafixers.types.DynamicOps; - -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.IntStream; -import java.util.stream.LongStream; -import java.util.stream.Stream; - -@SuppressWarnings("unused") -public abstract class DynamicLike { - protected final DynamicOps ops; - - public DynamicLike(final DynamicOps ops) { - this.ops = ops; - } - - public DynamicOps getOps() { - return ops; - } - - public abstract Optional asNumber(); - public abstract Optional asString(); - public abstract Optional>> asStreamOpt(); - public abstract Optional asByteBufferOpt(); - public abstract Optional asIntStreamOpt(); - public abstract Optional asLongStreamOpt(); - public abstract OptionalDynamic get(String key); - public abstract Optional getGeneric(T key); - public abstract Optional getElement(String key); - public abstract Optional getElementGeneric(T key); - public abstract Optional> asListOpt(Function, U> deserializer); - public abstract Optional> asMapOpt(Function, K> keyDeserializer, Function, V> valueDeserializer); - - public Number asNumber(final Number defaultValue) { - return asNumber().orElse(defaultValue); - } - - public int asInt(final int defaultValue) { - return asNumber(defaultValue).intValue(); - } - - public long asLong(final long defaultValue) { - return asNumber(defaultValue).longValue(); - } - - public float asFloat(final float defaultValue) { - return asNumber(defaultValue).floatValue(); - } - - public double asDouble(final double defaultValue) { - return asNumber(defaultValue).doubleValue(); - } - - public byte asByte(final byte defaultValue) { - return asNumber(defaultValue).byteValue(); - } - - public short asShort(final short defaultValue) { - return asNumber(defaultValue).shortValue(); - } - - public boolean asBoolean(final boolean defaultValue) { - return asNumber(defaultValue ? 1 : 0).intValue() != 0; - } - - public String asString(final String defaultValue) { - return asString().orElse(defaultValue); - } - - public Stream> asStream() { - return asStreamOpt().orElseGet(Stream::empty); - } - - public ByteBuffer asByteBuffer() { - return asByteBufferOpt().orElseGet(() -> ByteBuffer.wrap(new byte[0])); - } - - public IntStream asIntStream() { - return asIntStreamOpt().orElseGet(IntStream::empty); - } - - public LongStream asLongStream() { - return asLongStreamOpt().orElseGet(LongStream::empty); - } - - public List asList(final Function, U> deserializer) { - return asListOpt(deserializer).orElseGet(ImmutableList::of); - } - - public Map asMap(final Function, K> keyDeserializer, final Function, V> valueDeserializer) { - return asMapOpt(keyDeserializer, valueDeserializer).orElseGet(ImmutableMap::of); - } - - public T getElement(final String key, final T defaultValue) { - return getElement(key).orElse(defaultValue); - } - - public T getElementGeneric(final T key, final T defaultValue) { - return getElementGeneric(key).orElse(defaultValue); - } - - public Dynamic emptyList() { - return new Dynamic<>(ops, ops.emptyList()); - } - - public Dynamic emptyMap() { - return new Dynamic<>(ops, ops.emptyMap()); - } - - public Dynamic createNumeric(final Number i) { - return new Dynamic<>(ops, ops.createNumeric(i)); - } - - public Dynamic createByte(final byte value) { - return new Dynamic<>(ops, ops.createByte(value)); - } - - public Dynamic createShort(final short value) { - return new Dynamic<>(ops, ops.createShort(value)); - } - - public Dynamic createInt(final int value) { - return new Dynamic<>(ops, ops.createInt(value)); - } - - public Dynamic createLong(final long value) { - return new Dynamic<>(ops, ops.createLong(value)); - } - - public Dynamic createFloat(final float value) { - return new Dynamic<>(ops, ops.createFloat(value)); - } - - public Dynamic createDouble(final double value) { - return new Dynamic<>(ops, ops.createDouble(value)); - } - - public Dynamic createBoolean(final boolean value) { - return new Dynamic<>(ops, ops.createBoolean(value)); - } - - public Dynamic createString(final String value) { - return new Dynamic<>(ops, ops.createString(value)); - } - - public Dynamic createList(final Stream> input) { - return new Dynamic<>(ops, ops.createList(input.map(element -> element.cast(ops)))); - } - - public Dynamic createMap(final Map, ? extends Dynamic> map) { - final ImmutableMap.Builder builder = ImmutableMap.builder(); - for (final Map.Entry, ? extends Dynamic> entry : map.entrySet()) { - builder.put(entry.getKey().cast(ops), entry.getValue().cast(ops)); - } - return new Dynamic<>(ops, ops.createMap(builder.build())); - } - - public Dynamic createByteList(final ByteBuffer input) { - return new Dynamic<>(ops, ops.createByteList(input)); - } - - public Dynamic createIntList(final IntStream input) { - return new Dynamic<>(ops, ops.createIntList(input)); - } - - public Dynamic createLongList(final LongStream input) { - return new Dynamic<>(ops, ops.createLongList(input)); - } -} diff --git a/src/main/java/com/mojang/datafixers/FieldFinder.java b/src/main/java/com/mojang/datafixers/FieldFinder.java index 91e0dcdd..a20a9ded 100644 --- a/src/main/java/com/mojang/datafixers/FieldFinder.java +++ b/src/main/java/com/mojang/datafixers/FieldFinder.java @@ -2,8 +2,6 @@ // Licensed under the MIT license. package com.mojang.datafixers; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.optics.Adapter; import com.mojang.datafixers.optics.Optics; import com.mojang.datafixers.optics.Proj1; @@ -12,6 +10,8 @@ import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.templates.Tag; import com.mojang.datafixers.types.templates.TaggedChoice; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import javax.annotation.Nullable; import java.util.Objects; diff --git a/src/main/java/com/mojang/datafixers/FunctionType.java b/src/main/java/com/mojang/datafixers/FunctionType.java index ec1572db..364943e6 100644 --- a/src/main/java/com/mojang/datafixers/FunctionType.java +++ b/src/main/java/com/mojang/datafixers/FunctionType.java @@ -10,7 +10,6 @@ import com.mojang.datafixers.kinds.K1; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.kinds.Representable; -import com.mojang.datafixers.optics.Optic; import com.mojang.datafixers.optics.Optics; import com.mojang.datafixers.optics.Procompose; import com.mojang.datafixers.optics.Wander; @@ -69,7 +68,7 @@ enum Instance implements TraversalP, MonoidProfunctor TYPE_TOKEN = new TypeToken() {}; + public static final TypeToken TYPE_TOKEN = new TypeToken() {}; } @Override diff --git a/src/main/java/com/mojang/datafixers/NamedChoiceFinder.java b/src/main/java/com/mojang/datafixers/NamedChoiceFinder.java index 095829d3..22d9c311 100644 --- a/src/main/java/com/mojang/datafixers/NamedChoiceFinder.java +++ b/src/main/java/com/mojang/datafixers/NamedChoiceFinder.java @@ -2,10 +2,10 @@ // Licensed under the MIT license. package com.mojang.datafixers; -import com.mojang.datafixers.util.Either; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.templates.Tag; import com.mojang.datafixers.types.templates.TaggedChoice; +import com.mojang.datafixers.util.Either; import java.util.Objects; diff --git a/src/main/java/com/mojang/datafixers/OpticFinder.java b/src/main/java/com/mojang/datafixers/OpticFinder.java index cc4ada75..82cfcab5 100644 --- a/src/main/java/com/mojang/datafixers/OpticFinder.java +++ b/src/main/java/com/mojang/datafixers/OpticFinder.java @@ -2,8 +2,8 @@ // Licensed under the MIT license. package com.mojang.datafixers; -import com.mojang.datafixers.util.Either; import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.util.Either; import javax.annotation.Nullable; diff --git a/src/main/java/com/mojang/datafixers/OptionalDynamic.java b/src/main/java/com/mojang/datafixers/OptionalDynamic.java deleted file mode 100644 index 8177b5f1..00000000 --- a/src/main/java/com/mojang/datafixers/OptionalDynamic.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.mojang.datafixers; - -import com.mojang.datafixers.types.DynamicOps; - -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.IntStream; -import java.util.stream.LongStream; -import java.util.stream.Stream; - -@SuppressWarnings("unused") -public final class OptionalDynamic extends DynamicLike { - private final Optional> delegate; - - public OptionalDynamic(final DynamicOps ops, final Optional> delegate) { - super(ops); - this.delegate = delegate; - } - - public Optional> get() { - return delegate; - } - - public Optional map(Function, ? extends U> mapper) { - return delegate.map(mapper); - } - - public Optional flatMap(Function, Optional> mapper) { - return delegate.flatMap(mapper); - } - - @Override - public Optional asNumber() { - return flatMap(DynamicLike::asNumber); - } - - @Override - public Optional asString() { - return flatMap(DynamicLike::asString); - } - - @Override - public Optional>> asStreamOpt() { - return flatMap(DynamicLike::asStreamOpt); - } - - @Override - public Optional asByteBufferOpt() { - return flatMap(DynamicLike::asByteBufferOpt); - } - - @Override - public Optional asIntStreamOpt() { - return flatMap(DynamicLike::asIntStreamOpt); - } - - @Override - public Optional asLongStreamOpt() { - return flatMap(DynamicLike::asLongStreamOpt); - } - - @Override - public OptionalDynamic get(final String key) { - return new OptionalDynamic<>(ops, flatMap(k -> k.get(key).get())); - } - - @Override - public Optional getGeneric(final T key) { - return flatMap(v -> v.getGeneric(key)); - } - - @Override - public Optional getElement(final String key) { - return flatMap(v -> v.getElement(key)); - } - - @Override - public Optional getElementGeneric(final T key) { - return flatMap(v -> v.getElementGeneric(key)); - } - - @Override - public Optional> asListOpt(final Function, U> deserializer) { - return flatMap(t -> t.asListOpt(deserializer)); - } - - @Override - public Optional> asMapOpt(final Function, K> keyDeserializer, final Function, V> valueDeserializer) { - return flatMap(input -> input.asMapOpt(keyDeserializer, valueDeserializer)); - } - - public Dynamic orElseEmptyMap() { - return delegate.orElseGet(this::emptyMap); - } - - public Dynamic orElseEmptyList() { - return delegate.orElseGet(this::emptyList); - } -} diff --git a/src/main/java/com/mojang/datafixers/Products.java b/src/main/java/com/mojang/datafixers/Products.java new file mode 100644 index 00000000..ceba64cc --- /dev/null +++ b/src/main/java/com/mojang/datafixers/Products.java @@ -0,0 +1,781 @@ +package com.mojang.datafixers; + +import com.mojang.datafixers.kinds.App; +import com.mojang.datafixers.kinds.Applicative; +import com.mojang.datafixers.kinds.IdF; +import com.mojang.datafixers.kinds.K1; +import com.mojang.datafixers.util.Function10; +import com.mojang.datafixers.util.Function11; +import com.mojang.datafixers.util.Function12; +import com.mojang.datafixers.util.Function13; +import com.mojang.datafixers.util.Function14; +import com.mojang.datafixers.util.Function15; +import com.mojang.datafixers.util.Function16; +import com.mojang.datafixers.util.Function3; +import com.mojang.datafixers.util.Function4; +import com.mojang.datafixers.util.Function5; +import com.mojang.datafixers.util.Function6; +import com.mojang.datafixers.util.Function7; +import com.mojang.datafixers.util.Function8; +import com.mojang.datafixers.util.Function9; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Products { + final class P1 { + private final App t1; + + public P1(final App t1) { + this.t1 = t1; + } + + public App t1() { + return t1; + } + + public P2 and(final App t2) { + return new P2<>(t1, t2); + } + + public P3 and(final P2 p) { + return new P3<>(t1, p.t1, p.t2); + } + + public P4 and(final P3 p) { + return new P4<>(t1, p.t1, p.t2, p.t3); + } + + public P5 and(final P4 p) { + return new P5<>(t1, p.t1, p.t2, p.t3, p.t4); + } + + public P6 and(final P5 p) { + return new P6<>(t1, p.t1, p.t2, p.t3, p.t4, p.t5); + } + + public P7 and(final P6 p) { + return new P7<>(t1, p.t1, p.t2, p.t3, p.t4, p.t5, p.t6); + } + + public P8 and(final P7 p) { + return new P8<>(t1, p.t1, p.t2, p.t3, p.t4, p.t5, p.t6, p.t7); + } + + public App apply(final Applicative instance, final Function function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap(function, t1); + } + } + + static P2 of(final T1 t1, final T2 t2) { + return new P2<>(IdF.create(t1), IdF.create(t2)); + } + + final class P2 { + private final App t1; + private final App t2; + + public P2(final App t1, final App t2) { + this.t1 = t1; + this.t2 = t2; + } + + public App t1() { + return t1; + } + + public App t2() { + return t2; + } + + public P3 and(final App t3) { + return new P3<>(t1, t2, t3); + } + + public P4 and(final P2 p) { + return new P4<>(t1, t2, p.t1, p.t2); + } + + public P5 and(final P3 p) { + return new P5<>(t1, t2, p.t1, p.t2, p.t3); + } + + public P6 and(final P4 p) { + return new P6<>(t1, t2, p.t1, p.t2, p.t3, p.t4); + } + + public P7 and(final P5 p) { + return new P7<>(t1, t2, p.t1, p.t2, p.t3, p.t4, p.t5); + } + + public P8 and(final P6 p) { + return new P8<>(t1, t2, p.t1, p.t2, p.t3, p.t4, p.t5, p.t6); + } + + public App apply(final Applicative instance, final BiFunction function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap2(function, t1, t2); + } + } + + final class P3 { + private final App t1; + private final App t2; + private final App t3; + + public P3(final App t1, final App t2, final App t3) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + } + + public App t1() { + return t1; + } + + public App t2() { + return t2; + } + + public App t3() { + return t3; + } + + public P4 and(final App t4) { + return new P4<>(t1, t2, t3, t4); + } + + public P5 and(final P2 p) { + return new P5<>(t1, t2, t3, p.t1, p.t2); + } + + public P6 and(final P3 p) { + return new P6<>(t1, t2, t3, p.t1, p.t2, p.t3); + } + + public P7 and(final P4 p) { + return new P7<>(t1, t2, t3, p.t1, p.t2, p.t3, p.t4); + } + + public P8 and(final P5 p) { + return new P8<>(t1, t2, t3, p.t1, p.t2, p.t3, p.t4, p.t5); + } + + public App apply(final Applicative instance, final Function3 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap3(function, t1, t2, t3); + } + } + + final class P4 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + + public P4(final App t1, final App t2, final App t3, final App t4) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + } + + public App t1() { + return t1; + } + + public App t2() { + return t2; + } + + public App t3() { + return t3; + } + + public App t4() { + return t4; + } + + public P5 and(final App t5) { + return new P5<>(t1, t2, t3, t4, t5); + } + + public P6 and(final P2 p) { + return new P6<>(t1, t2, t3, t4, p.t1, p.t2); + } + + public P7 and(final P3 p) { + return new P7<>(t1, t2, t3, t4, p.t1, p.t2, p.t3); + } + + public P8 and(final P4 p) { + return new P8<>(t1, t2, t3, t4, p.t1, p.t2, p.t3, p.t4); + } + + public App apply(final Applicative instance, final Function4 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap4(function, t1, t2, t3, t4); + } + } + + final class P5 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + + public P5(final App t1, final App t2, final App t3, final App t4, final App t5) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + } + + public App t1() { + return t1; + } + + public App t2() { + return t2; + } + + public App t3() { + return t3; + } + + public App t4() { + return t4; + } + + public App t5() { + return t5; + } + + public P6 and(final App t6) { + return new P6<>(t1, t2, t3, t4, t5, t6); + } + + public P7 and(final P2 p) { + return new P7<>(t1, t2, t3, t4, t5, p.t1, p.t2); + } + + public P8 and(final P3 p) { + return new P8<>(t1, t2, t3, t4, t5, p.t1, p.t2, p.t3); + } + + public App apply(final Applicative instance, final Function5 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap5(function, t1, t2, t3, t4, t5); + } + } + + final class P6 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + private final App t6; + + public P6(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + this.t6 = t6; + } + + public App t1() { + return t1; + } + + public App t2() { + return t2; + } + + public App t3() { + return t3; + } + + public App t4() { + return t4; + } + + public App t5() { + return t5; + } + + public App t6() { + return t6; + } + + public P7 and(final App t7) { + return new P7<>(t1, t2, t3, t4, t5, t6, t7); + } + + public P8 and(final P2 p) { + return new P8<>(t1, t2, t3, t4, t5, t6, p.t1, p.t2); + } + + public App apply(final Applicative instance, final Function6 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap6(function, t1, t2, t3, t4, t5, t6); + } + } + + final class P7 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + private final App t6; + private final App t7; + + public P7(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + this.t6 = t6; + this.t7 = t7; + } + + public App t1() { + return t1; + } + + public App t2() { + return t2; + } + + public App t3() { + return t3; + } + + public App t4() { + return t4; + } + + public App t5() { + return t5; + } + + public App t6() { + return t6; + } + + public App t7() { + return t7; + } + + public P8 and(final App t8) { + return new P8<>(t1, t2, t3, t4, t5, t6, t7, t8); + } + + public App apply(final Applicative instance, final Function7 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap7(function, t1, t2, t3, t4, t5, t6, t7); + } + } + + final class P8 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + private final App t6; + private final App t7; + private final App t8; + + public P8(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + this.t6 = t6; + this.t7 = t7; + this.t8 = t8; + } + + public App t1() { + return t1; + } + + public App t2() { + return t2; + } + + public App t3() { + return t3; + } + + public App t4() { + return t4; + } + + public App t5() { + return t5; + } + + public App t6() { + return t6; + } + + public App t7() { + return t7; + } + + public App t8() { + return t8; + } + + public App apply(final Applicative instance, final Function8 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap8(function, t1, t2, t3, t4, t5, t6, t7, t8); + } + } + + final class P9 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + private final App t6; + private final App t7; + private final App t8; + private final App t9; + + public P9(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + this.t6 = t6; + this.t7 = t7; + this.t8 = t8; + this.t9 = t9; + } + + public App apply(final Applicative instance, final Function9 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap9(function, t1, t2, t3, t4, t5, t6, t7, t8, t9); + } + } + + final class P10 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + private final App t6; + private final App t7; + private final App t8; + private final App t9; + private final App t10; + + public P10(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + this.t6 = t6; + this.t7 = t7; + this.t8 = t8; + this.t9 = t9; + this.t10 = t10; + } + + public App apply(final Applicative instance, final Function10 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap10(function, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); + } + } + + final class P11 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + private final App t6; + private final App t7; + private final App t8; + private final App t9; + private final App t10; + private final App t11; + + public P11(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + this.t6 = t6; + this.t7 = t7; + this.t8 = t8; + this.t9 = t9; + this.t10 = t10; + this.t11 = t11; + } + + public App apply(final Applicative instance, final Function11 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap11(function, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + } + + final class P12 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + private final App t6; + private final App t7; + private final App t8; + private final App t9; + private final App t10; + private final App t11; + private final App t12; + + public P12(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + this.t6 = t6; + this.t7 = t7; + this.t8 = t8; + this.t9 = t9; + this.t10 = t10; + this.t11 = t11; + this.t12 = t12; + } + + public App apply(final Applicative instance, final Function12 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap12(function, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + } + + final class P13 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + private final App t6; + private final App t7; + private final App t8; + private final App t9; + private final App t10; + private final App t11; + private final App t12; + private final App t13; + + public P13(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + this.t6 = t6; + this.t7 = t7; + this.t8 = t8; + this.t9 = t9; + this.t10 = t10; + this.t11 = t11; + this.t12 = t12; + this.t13 = t13; + } + + public App apply(final Applicative instance, final Function13 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap13(function, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + } + + final class P14 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + private final App t6; + private final App t7; + private final App t8; + private final App t9; + private final App t10; + private final App t11; + private final App t12; + private final App t13; + private final App t14; + + public P14(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13, final App t14) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + this.t6 = t6; + this.t7 = t7; + this.t8 = t8; + this.t9 = t9; + this.t10 = t10; + this.t11 = t11; + this.t12 = t12; + this.t13 = t13; + this.t14 = t14; + } + + public App apply(final Applicative instance, final Function14 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap14(function, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + } + + final class P15 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + private final App t6; + private final App t7; + private final App t8; + private final App t9; + private final App t10; + private final App t11; + private final App t12; + private final App t13; + private final App t14; + private final App t15; + + public P15(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13, final App t14, final App t15) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + this.t6 = t6; + this.t7 = t7; + this.t8 = t8; + this.t9 = t9; + this.t10 = t10; + this.t11 = t11; + this.t12 = t12; + this.t13 = t13; + this.t14 = t14; + this.t15 = t15; + } + + public App apply(final Applicative instance, final Function15 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap15(function, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + } + + final class P16 { + private final App t1; + private final App t2; + private final App t3; + private final App t4; + private final App t5; + private final App t6; + private final App t7; + private final App t8; + private final App t9; + private final App t10; + private final App t11; + private final App t12; + private final App t13; + private final App t14; + private final App t15; + private final App t16; + + public P16(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13, final App t14, final App t15, final App t16) { + this.t1 = t1; + this.t2 = t2; + this.t3 = t3; + this.t4 = t4; + this.t5 = t5; + this.t6 = t6; + this.t7 = t7; + this.t8 = t8; + this.t9 = t9; + this.t10 = t10; + this.t11 = t11; + this.t12 = t12; + this.t13 = t13; + this.t14 = t14; + this.t15 = t15; + this.t16 = t16; + } + + public App apply(final Applicative instance, final Function16 function) { + return apply(instance, instance.point(function)); + } + + public App apply(final Applicative instance, final App> function) { + return instance.ap16(function, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + } +} diff --git a/src/main/java/com/mojang/datafixers/Typed.java b/src/main/java/com/mojang/datafixers/Typed.java index a6195abd..55c55beb 100644 --- a/src/main/java/com/mojang/datafixers/Typed.java +++ b/src/main/java/com/mojang/datafixers/Typed.java @@ -4,8 +4,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.kinds.Const; import com.mojang.datafixers.kinds.IdF; import com.mojang.datafixers.kinds.Monoid; @@ -17,9 +15,13 @@ import com.mojang.datafixers.optics.ReForgetC; import com.mojang.datafixers.optics.Traversal; import com.mojang.datafixers.optics.profunctors.TraversalP; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.templates.RecursivePoint; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.DynamicOps; import java.util.List; import java.util.Optional; @@ -165,7 +167,7 @@ private Typed updateCap(final TypedOptic field, fin public List> getAllTyped(final OpticFinder optic) { final TypedOptic field = optic.findType(type, optic.type(), false).orThrow(); - return getAll(field).stream().map(ft -> new Typed(optic.type(), ops, ft)).collect(Collectors.toList()); + return getAll(field).stream().map(ft -> new Typed<>(optic.type(), ops, ft)).collect(Collectors.toList()); } public List getAll(final TypedOptic field) { @@ -205,7 +207,7 @@ public A getValue() { return value; } - public Dynamic write() { + public DataResult> write() { return type.writeDynamic(ops, value); } } diff --git a/src/main/java/com/mojang/datafixers/TypedOptic.java b/src/main/java/com/mojang/datafixers/TypedOptic.java index 375a4137..10c55657 100644 --- a/src/main/java/com/mojang/datafixers/TypedOptic.java +++ b/src/main/java/com/mojang/datafixers/TypedOptic.java @@ -5,8 +5,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K1; @@ -25,8 +23,11 @@ import com.mojang.datafixers.optics.profunctors.TraversalP; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.templates.TaggedChoice; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -167,7 +168,7 @@ public static TypedOptic, Either, G, G2> inj2(fin ); } - public static TypedOptic>, java.util.List>, K, K2> compoundListKeys(final Type aType, final Type bType, final Type valueType) { + public static TypedOptic>, List>, K, K2> compoundListKeys(final Type aType, final Type bType, final Type valueType) { return new TypedOptic<>( TraversalP.Mu.TYPE_TOKEN, DSL.compoundList(aType, valueType), @@ -178,7 +179,7 @@ public static TypedOptic>, java.util.List

TypedOptic>, java.util.List>, V, V2> compoundListElements(final Type keyType, final Type aType, final Type bType) { + public static TypedOptic>, List>, V, V2> compoundListElements(final Type keyType, final Type aType, final Type bType) { return new TypedOptic<>( TraversalP.Mu.TYPE_TOKEN, DSL.compoundList(keyType, aType), @@ -189,7 +190,7 @@ public static TypedOptic>, java.util.List

extends Profunctor { static

Cartesian unbox(final App proofBox) { diff --git a/src/main/java/com/mojang/datafixers/optics/profunctors/Cocartesian.java b/src/main/java/com/mojang/datafixers/optics/profunctors/Cocartesian.java index 2135dc18..4d1da3d7 100644 --- a/src/main/java/com/mojang/datafixers/optics/profunctors/Cocartesian.java +++ b/src/main/java/com/mojang/datafixers/optics/profunctors/Cocartesian.java @@ -3,12 +3,12 @@ package com.mojang.datafixers.optics.profunctors; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.util.Either; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.CocartesianLike; import com.mojang.datafixers.kinds.K1; import com.mojang.datafixers.kinds.K2; +import com.mojang.datafixers.util.Either; public interface Cocartesian

extends Profunctor { static

Cocartesian unbox(final App proofBox) { diff --git a/src/main/java/com/mojang/datafixers/optics/profunctors/Monoidal.java b/src/main/java/com/mojang/datafixers/optics/profunctors/Monoidal.java index 99d70179..1f991043 100644 --- a/src/main/java/com/mojang/datafixers/optics/profunctors/Monoidal.java +++ b/src/main/java/com/mojang/datafixers/optics/profunctors/Monoidal.java @@ -2,10 +2,10 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics.profunctors; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; +import com.mojang.datafixers.util.Pair; import java.util.function.Supplier; diff --git a/src/main/java/com/mojang/datafixers/optics/profunctors/ReCartesian.java b/src/main/java/com/mojang/datafixers/optics/profunctors/ReCartesian.java index ef9704b2..995372db 100644 --- a/src/main/java/com/mojang/datafixers/optics/profunctors/ReCartesian.java +++ b/src/main/java/com/mojang/datafixers/optics/profunctors/ReCartesian.java @@ -2,10 +2,10 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics.profunctors; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; +import com.mojang.datafixers.util.Pair; public interface ReCartesian

extends Profunctor { static

ReCartesian unbox(final App proofBox) { diff --git a/src/main/java/com/mojang/datafixers/optics/profunctors/ReCocartesian.java b/src/main/java/com/mojang/datafixers/optics/profunctors/ReCocartesian.java index fa144123..8b6d86fa 100644 --- a/src/main/java/com/mojang/datafixers/optics/profunctors/ReCocartesian.java +++ b/src/main/java/com/mojang/datafixers/optics/profunctors/ReCocartesian.java @@ -2,10 +2,10 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics.profunctors; -import com.mojang.datafixers.util.Either; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; +import com.mojang.datafixers.util.Either; public interface ReCocartesian

extends Profunctor { static

ReCocartesian unbox(final App proofBox) { diff --git a/src/main/java/com/mojang/datafixers/optics/profunctors/TraversalP.java b/src/main/java/com/mojang/datafixers/optics/profunctors/TraversalP.java index 3f3daee9..2dd262c1 100644 --- a/src/main/java/com/mojang/datafixers/optics/profunctors/TraversalP.java +++ b/src/main/java/com/mojang/datafixers/optics/profunctors/TraversalP.java @@ -3,8 +3,6 @@ package com.mojang.datafixers.optics.profunctors; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; @@ -13,6 +11,8 @@ import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.kinds.Traversable; import com.mojang.datafixers.optics.Wander; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; public interface TraversalP

extends AffineP/*, Monoidal*/ { static

TraversalP unbox(final App proofBox) { @@ -54,12 +54,12 @@ public FunctionType, App>> wander(final Ap @Override default App2, Pair> first(final App2 input) { - return dimap(traverse(new Pair.Instance(), input), box -> box, Pair::unbox); + return dimap(traverse(new Pair.Instance<>(), input), box -> box, Pair::unbox); } @Override default App2, Either> left(final App2 input) { - return dimap(traverse(new Either.Instance(), input), box -> box, Either::unbox); + return dimap(traverse(new Either.Instance<>(), input), box -> box, Either::unbox); } default FunctorProfunctor> toFP3() { diff --git a/src/main/java/com/mojang/datafixers/types/DynamicOps.java b/src/main/java/com/mojang/datafixers/types/DynamicOps.java deleted file mode 100644 index 010595f5..00000000 --- a/src/main/java/com/mojang/datafixers/types/DynamicOps.java +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types; - -import com.google.common.collect.ImmutableMap; -import com.mojang.datafixers.Dynamic; - -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.LongStream; -import java.util.stream.Stream; - -public interface DynamicOps { - T empty(); - - default T emptyMap() { - return createMap(ImmutableMap.of()); - } - - default T emptyList() { - return createList(Stream.empty()); - } - - Type getType(final T input); - - default Optional cast(final T input, final Type type) { - if (type == getType(input)) { - return Optional.of(type.readTyped(new Dynamic<>(this, input)).getSecond().orElseThrow(() -> new IllegalStateException("Parse error during dynamic cast")).getValue()); - } - return Optional.empty(); - } - - Optional getNumberValue(T input); - - default Number getNumberValue(final T input, final Number defaultValue) { - return getNumberValue(input).orElse(defaultValue); - } - - T createNumeric(Number i); - - default T createByte(final byte value) { - return createNumeric(value); - } - - default T createShort(final short value) { - return createNumeric(value); - } - - default T createInt(final int value) { - return createNumeric(value); - } - - default T createLong(final long value) { - return createNumeric(value); - } - - default T createFloat(final float value) { - return createNumeric(value); - } - - default T createDouble(final double value) { - return createNumeric(value); - } - - default Optional getBooleanValue(final T input) { - return getNumberValue(input).map(number -> number.byteValue() != 0); - } - - default T createBoolean(final boolean value) { - return createByte((byte) (value ? 1 : 0)); - } - - Optional getStringValue(T input); - - T createString(String value); - - /** - * keeps input unchanged if it's not list-like - */ - T mergeInto(T input, T value); - - /** - * keeps input unchanged if it's not map-like - */ - T mergeInto(T input, T key, T value); - - /** - * merges 2 values together, if possible (list + list, map + map, empty + anything) - */ - T merge(T first, T second); - - Optional> getMapValues(T input); - - T createMap(Map map); - - Optional> getStream(T input); - - T createList(Stream input); - - default Optional getByteBuffer(final T input) { - return getStream(input).flatMap(stream -> { - final List list = stream.collect(Collectors.toList()); - if (list.stream().allMatch(element -> getNumberValue(element).isPresent())) { - final ByteBuffer buffer = ByteBuffer.wrap(new byte[list.size()]); - for (int i = 0; i < list.size(); i++) { - buffer.put(i, getNumberValue(list.get(i)).get().byteValue()); - } - return Optional.of(buffer); - } - return Optional.empty(); - }); - } - - default T createByteList(final ByteBuffer input) { - final int[] i = {0}; - return createList(Stream.generate(() -> createByte(input.get(i[0]++))).limit(input.capacity())); - } - - default Optional getIntStream(final T input) { - return getStream(input).flatMap(stream -> { - final List list = stream.collect(Collectors.toList()); - if (list.stream().allMatch(element -> getNumberValue(element).isPresent())) { - return Optional.of(list.stream().mapToInt(element -> getNumberValue(element).get().intValue())); - } - return Optional.empty(); - }); - } - - default T createIntList(final IntStream input) { - return createList(input.mapToObj(this::createInt)); - } - - default Optional getLongStream(final T input) { - return getStream(input).flatMap(stream -> { - final List list = stream.collect(Collectors.toList()); - if (list.stream().allMatch(element -> getNumberValue(element).isPresent())) { - return Optional.of(list.stream().mapToLong(element -> getNumberValue(element).get().longValue())); - } - return Optional.empty(); - }); - } - - default T createLongList(final LongStream input) { - return createList(input.mapToObj(this::createLong)); - } - - 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 getMapValues(input).flatMap(map -> Optional.ofNullable(map.get(key))); - } - - default T set(final T input, final String key, final T value) { - return mergeInto(input, createString(key), value); - } - - 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).map(value -> mergeInto(input, key, function.apply(value))).orElse(input); - } -} diff --git a/src/main/java/com/mojang/datafixers/types/Func.java b/src/main/java/com/mojang/datafixers/types/Func.java index 2d943bce..c7089409 100644 --- a/src/main/java/com/mojang/datafixers/types/Func.java +++ b/src/main/java/com/mojang/datafixers/types/Func.java @@ -2,11 +2,12 @@ // Licensed under the MIT license. package com.mojang.datafixers.types; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.types.templates.TypeTemplate; +import com.mojang.serialization.Codec; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.Encoder; import java.util.Objects; -import java.util.Optional; import java.util.function.Function; public final class Func extends Type> { @@ -24,13 +25,11 @@ public TypeTemplate buildTemplate() { } @Override - public Pair>> read(final DynamicOps ops, final T input) { - return Pair.of(input, Optional.empty()); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Function value) { - return rest; + protected Codec> buildCodec() { + return Codec.of( + Encoder.error("Cannot save a function"), + Decoder.error("Cannot read a function") + ); } @Override @@ -40,10 +39,10 @@ public String toString() { @Override public boolean equals(final Object obj, final boolean ignoreRecursionPoints, final boolean checkIndex) { - if (!(obj instanceof com.mojang.datafixers.types.Func)) { + if (!(obj instanceof Func)) { return false; } - final com.mojang.datafixers.types.Func that = (com.mojang.datafixers.types.Func) obj; + final Func that = (Func) obj; return first.equals(that.first, ignoreRecursionPoints, checkIndex) && second.equals(that.second, ignoreRecursionPoints, checkIndex); } diff --git a/src/main/java/com/mojang/datafixers/types/JsonOps.java b/src/main/java/com/mojang/datafixers/types/JsonOps.java deleted file mode 100644 index 8ef11236..00000000 --- a/src/main/java/com/mojang/datafixers/types/JsonOps.java +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonNull; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import com.mojang.datafixers.DSL; -import com.mojang.datafixers.util.Pair; - -import java.math.BigDecimal; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -public class JsonOps implements DynamicOps { - public static final JsonOps INSTANCE = new JsonOps(); - - protected JsonOps() { - } - - @Override - public JsonElement empty() { - return JsonNull.INSTANCE; - } - - @Override - public Type getType(final JsonElement input) { - if (input.isJsonObject()) { - return DSL.compoundList(DSL.remainderType(), DSL.remainderType()); - } - if (input.isJsonArray()) { - return DSL.list(DSL.remainderType()); - } - if (input.isJsonNull()) { - return DSL.nilType(); - } - final JsonPrimitive primitive = input.getAsJsonPrimitive(); - if (primitive.isString()) { - return DSL.string(); - } - if (primitive.isBoolean()) { - return DSL.bool(); - } - final BigDecimal value = primitive.getAsBigDecimal(); - try { - final long l = value.longValueExact(); - if ((byte) l == l) { - return DSL.byteType(); - } - if ((short) l == l) { - return DSL.shortType(); - } - if ((int) l == l) { - return DSL.intType(); - } - return DSL.longType(); - } catch (final ArithmeticException e) { - final double d = value.doubleValue(); - if ((float) d == d) { - return DSL.floatType(); - } - return DSL.doubleType(); - } - } - - @Override - public Optional getNumberValue(final JsonElement input) { - if (input.isJsonPrimitive()) { - if (input.getAsJsonPrimitive().isNumber()) { - return Optional.of(input.getAsNumber()); - } else if (input.getAsJsonPrimitive().isBoolean()) { - return Optional.of(input.getAsBoolean() ? 1 : 0); - } - } - return Optional.empty(); - } - - @Override - public JsonElement createNumeric(final Number i) { - return new JsonPrimitive(i); - } - - @Override - public Optional getBooleanValue(final JsonElement input) { - if (input.isJsonPrimitive()) { - if (input.getAsJsonPrimitive().isBoolean()) { - return Optional.of(input.getAsBoolean()); - } else if (input.getAsJsonPrimitive().isNumber()) { - return Optional.of(input.getAsNumber().byteValue() != 0); - } - } - return Optional.empty(); - } - - @Override - public JsonElement createBoolean(final boolean value) { - return new JsonPrimitive(value); - } - - @Override - public Optional getStringValue(final JsonElement input) { - if (input.isJsonPrimitive() && input.getAsJsonPrimitive().isString()) { - return Optional.of(input.getAsString()); - } - return Optional.empty(); - } - - @Override - public JsonElement createString(final String value) { - return new JsonPrimitive(value); - } - - @Override - public JsonElement mergeInto(final JsonElement input, final JsonElement value) { - final JsonArray result; - if (value.isJsonNull()) { - return input; - } - if (input.isJsonObject()) { - if (value.isJsonObject()) { - final JsonObject resultObject = new JsonObject(); - final JsonObject first = input.getAsJsonObject(); - for (final Map.Entry entry : first.entrySet()) { - resultObject.add(entry.getKey(), entry.getValue()); - } - final JsonObject second = value.getAsJsonObject(); - for (final Map.Entry entry : second.entrySet()) { - resultObject.add(entry.getKey(), entry.getValue()); - } - return resultObject; - } - return input; - } else if (input.isJsonNull()) { - throw new IllegalArgumentException("mergeInto called with null input."); - } else if (input.isJsonArray()) { - result = new JsonArray(); - StreamSupport.stream(input.getAsJsonArray().spliterator(), false).forEach(result::add); - } else { - return input; - } - result.add(value); - return result; - } - - @Override - public JsonElement mergeInto(final JsonElement input, final JsonElement key, final JsonElement value) { - final JsonObject output; - if (input.isJsonNull()) { - output = new JsonObject(); - } else if (input.isJsonObject()) { - output = new JsonObject(); - input.getAsJsonObject().entrySet().forEach(entry -> output.add(entry.getKey(), entry.getValue())); - } else { - return input; - } - output.add(key.getAsString(), value); - return output; - } - - @Override - public JsonElement merge(final JsonElement first, final JsonElement second) { - if (first.isJsonNull()) { - return second; - } - if (second.isJsonNull()) { - return first; - } - if (first.isJsonObject() && second.isJsonObject()) { - JsonObject result = new JsonObject(); - first.getAsJsonObject().entrySet().forEach(entry -> result.add(entry.getKey(), entry.getValue())); - second.getAsJsonObject().entrySet().forEach(entry -> result.add(entry.getKey(), entry.getValue())); - return result; - } - if (first.isJsonArray() && second.isJsonArray()) { - JsonArray result = new JsonArray(); - first.getAsJsonArray().forEach(result::add); - second.getAsJsonArray().forEach(result::add); - return result; - } - throw new IllegalArgumentException("Could not merge " + first + " and " + second); - } - - @Override - public Optional> getMapValues(final JsonElement input) { - if (input.isJsonObject()) { - return Optional.of(input.getAsJsonObject().entrySet().stream().map(entry -> Pair.of(new JsonPrimitive(entry.getKey()), entry.getValue())).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond))); - } - return Optional.empty(); - } - - @Override - public JsonElement createMap(final Map map) { - final JsonObject result = new JsonObject(); - for (final Map.Entry entry : map.entrySet()) { - result.add(entry.getKey().getAsString(), entry.getValue()); - } - return result; - } - - @Override - public Optional> getStream(final JsonElement input) { - if (input.isJsonArray()) { - return Optional.of(StreamSupport.stream(input.getAsJsonArray().spliterator(), false)); - } - return Optional.empty(); - } - - @Override - public JsonElement createList(final Stream input) { - final JsonArray result = new JsonArray(); - input.forEach(result::add); - return result; - } - - @Override - public JsonElement remove(final JsonElement input, final String key) { - if (input.isJsonObject()) { - 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; - } - - @Override - public String toString() { - return "JSON"; - } -} diff --git a/src/main/java/com/mojang/datafixers/types/Type.java b/src/main/java/com/mojang/datafixers/types/Type.java index 2f44051b..4592da3c 100644 --- a/src/main/java/com/mojang/datafixers/types/Type.java +++ b/src/main/java/com/mojang/datafixers/types/Type.java @@ -5,7 +5,6 @@ import com.google.common.collect.Maps; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFixUtils; -import com.mojang.datafixers.Dynamic; import com.mojang.datafixers.FieldFinder; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.OpticFinder; @@ -23,6 +22,11 @@ import com.mojang.datafixers.types.templates.TypeTemplate; import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.DynamicOps; +import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.tuple.Triple; import javax.annotation.Nullable; @@ -30,7 +34,6 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; public abstract class Type implements App { private static final Map, TypeRewriteRule, PointFreeRule>, CompletableFuture>>> PENDING_REWRITE_CACHE = Maps.newConcurrentMap(); @@ -45,6 +48,9 @@ public static Type unbox(final App box) { @Nullable private TypeTemplate template; + @Nullable + private Codec codec; + public RewriteResult rewriteOrNop(final TypeRewriteRule rule) { return DataFixUtils.orElseGet(rule.rewrite(this), () -> RewriteResult.nop(this)); } @@ -106,55 +112,59 @@ public Optional> findCheckedType(final int index) { return Optional.empty(); } - public final Pair, Optional> read(final Dynamic input) { - return read(input.getOps(), input.getValue()).mapFirst(v -> new Dynamic<>(input.getOps(), v)); + public final DataResult>> read(final Dynamic input) { + return codec().decode(input.getOps(), input.getValue()).map(v -> v.mapSecond(t -> new Dynamic<>(input.getOps(), t))); } - public abstract Pair> read(final DynamicOps ops, final T input); - - public abstract T write(final DynamicOps ops, final T rest, final A value); - - public final T write(final DynamicOps ops, final A value) { - return write(ops, ops.empty(), value); + public final Codec codec() { + if (codec == null) { + codec = buildCodec(); + } + return codec; } - public final Dynamic writeDynamic(final DynamicOps ops, final T rest, final A value) { - return new Dynamic<>(ops, write(ops, rest, value)); + protected abstract Codec buildCodec(); + + public final DataResult write(final DynamicOps ops, final A value) { + return codec().encode(value, ops, ops.empty()); } - public final Dynamic writeDynamic(final DynamicOps ops, final A value) { - return new Dynamic<>(ops, write(ops, value)); + public final DataResult> writeDynamic(final DynamicOps ops, final A value) { + return write(ops, value).map(result -> new Dynamic<>(ops, result)); } - public Pair>> readTyped(final Dynamic input) { + public DataResult, T>> readTyped(final Dynamic input) { return readTyped(input.getOps(), input.getValue()); } - public Pair>> readTyped(final DynamicOps ops, final T input) { - return read(ops, input).mapSecond(vo -> vo.map(v -> new Typed<>(this, ops, v))); + public DataResult, T>> readTyped(final DynamicOps ops, final T input) { + return codec().decode(ops, input).map(vo -> vo.mapFirst(v -> new Typed<>(this, ops, v))); } - public Pair> read(final DynamicOps ops, final TypeRewriteRule rule, final PointFreeRule fRule, final T input) { - return read(ops, input).mapSecond(vo -> vo.map(v -> + public DataResult, T>> read(final DynamicOps ops, final TypeRewriteRule rule, final PointFreeRule fRule, final T input) { + return codec().decode(ops, input).map(vo -> vo.mapFirst(v -> rewrite(rule, fRule).map(r -> r.view().function().evalCached().apply(ops).apply(v) ) )); } - public Optional readAndWrite(final DynamicOps ops, final Type expectedType, final TypeRewriteRule rule, final PointFreeRule fRule, final T input) { - final Pair> po = read(ops, input); - return po.getSecond().flatMap(v -> - rewrite(rule, fRule).map(r -> - capWrite(ops, expectedType, po.getFirst(), v, r.view()) - ) + public DataResult readAndWrite(final DynamicOps ops, final Type expectedType, final TypeRewriteRule rule, final PointFreeRule fRule, final T input) { + final Optional> rewriteResult = rewrite(rule, fRule); + if (!rewriteResult.isPresent()) { + return DataResult.error("Could not build a rewrite rule: " + rule + " " + fRule, input); + } + final View view = rewriteResult.get().view(); + + return codec().decode(ops, input).flatMap(pair -> + capWrite(ops, expectedType, pair.getSecond(), pair.getFirst(), view) ); } - public T capWrite(final DynamicOps ops, final Type expectedType, final T rest, final A value, final View f) { + private DataResult capWrite(final DynamicOps ops, final Type expectedType, final T rest, final A value, final View f) { if (!expectedType.equals(f.newType(), true, true)) { - throw new IllegalStateException("Rewritten type doesn't match."); + return DataResult.error("Rewritten type doesn't match"); } - return f.newType().write(ops, rest, f.function().evalCached().apply(ops).apply(value)); + return f.newType().codec().encode(f.function().evalCached().apply(ops).apply(value), ops, rest); } @SuppressWarnings("unchecked") @@ -167,15 +177,16 @@ public T capWrite(final DynamicOps ops, final Type expectedType, fi if (rewrite != null) { return (Optional>) rewrite; } - final AtomicReference>>> ref = new AtomicReference<>(); + // TODO: AtomicReference.getPlain/setPlain in java9+ + final MutableObject>>> ref = new MutableObject<>(); final CompletableFuture>> pending = PENDING_REWRITE_CACHE.computeIfAbsent(key, k -> { final CompletableFuture>> value = new CompletableFuture<>(); - ref.set(value); + ref.setValue(value); return value; }); - if (ref.get() != null) { + if (ref.getValue() != null) { Optional> result = rule.rewrite(this).flatMap(r -> r.view().rewrite(fRule).map(view -> RewriteResult.create(view, r.recData()))); REWRITE_CACHE.put(key, result); pending.complete(result); diff --git a/src/main/java/com/mojang/datafixers/types/constant/BoolType.java b/src/main/java/com/mojang/datafixers/types/constant/BoolType.java deleted file mode 100644 index 8a0f22e4..00000000 --- a/src/main/java/com/mojang/datafixers/types/constant/BoolType.java +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types.constant; - -import com.mojang.datafixers.util.Pair; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.templates.Const; - -import java.util.Optional; - -public final class BoolType extends Const.ConstType { - @Override - public Pair> read(final DynamicOps ops, final T input) { - return ops - .getNumberValue(input) - .map(v -> Pair.of(ops.empty(), Optional.of(v.intValue() != 0))) - .orElseGet(() -> Pair.of(input, Optional.empty())); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Boolean value) { - return ops.createBoolean(value); - } - - @Override - public String toString() { - return "Bool"; - } -} diff --git a/src/main/java/com/mojang/datafixers/types/constant/ByteType.java b/src/main/java/com/mojang/datafixers/types/constant/ByteType.java deleted file mode 100644 index 03878a5f..00000000 --- a/src/main/java/com/mojang/datafixers/types/constant/ByteType.java +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types.constant; - -import com.mojang.datafixers.util.Pair; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.templates.Const; - -import java.util.Optional; - -public final class ByteType extends Const.ConstType { - @Override - public Pair> read(final DynamicOps ops, final T input) { - return ops - .getNumberValue(input) - .map(v -> Pair.of(ops.empty(), Optional.of(v.byteValue()))) - .orElseGet(() -> Pair.of(input, Optional.empty())); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Byte value) { - return ops.createByte(value); - } - - @Override - public String toString() { - return "Byte"; - } -} diff --git a/src/main/java/com/mojang/datafixers/types/constant/DoubleType.java b/src/main/java/com/mojang/datafixers/types/constant/DoubleType.java deleted file mode 100644 index 86777ca5..00000000 --- a/src/main/java/com/mojang/datafixers/types/constant/DoubleType.java +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types.constant; - -import com.mojang.datafixers.util.Pair; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.templates.Const; - -import java.util.Optional; - -public final class DoubleType extends Const.ConstType { - @Override - public Pair> read(final DynamicOps ops, final T input) { - return ops - .getNumberValue(input) - .map(v -> Pair.of(ops.empty(), Optional.of(v.doubleValue()))) - .orElseGet(() -> Pair.of(input, Optional.empty())); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Double value) { - return ops.createDouble(value); - } - - @Override - public String toString() { - return "Double"; - } -} diff --git a/src/main/java/com/mojang/datafixers/types/constant/EmptyPart.java b/src/main/java/com/mojang/datafixers/types/constant/EmptyPart.java new file mode 100644 index 00000000..0c12f6e3 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/types/constant/EmptyPart.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.datafixers.types.constant; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.types.templates.TypeTemplate; +import com.mojang.datafixers.util.Unit; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DynamicOps; + +import java.util.Optional; + +public final class EmptyPart extends Type { + @Override + public String toString() { + return "EmptyPart"; + } + + @Override + public Optional point(final DynamicOps ops) { + return Optional.of(Unit.INSTANCE); + } + + @Override + public boolean equals(final Object o, final boolean ignoreRecursionPoints, final boolean checkIndex) { + return this == o; + } + + @Override + public TypeTemplate buildTemplate() { + return DSL.constType(this); + } + + @Override + protected Codec buildCodec() { + return Codec.EMPTY.codec(); + } +} diff --git a/src/main/java/com/mojang/datafixers/types/constant/EmptyPartPassthrough.java b/src/main/java/com/mojang/datafixers/types/constant/EmptyPartPassthrough.java new file mode 100644 index 00000000..2bfadf11 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/types/constant/EmptyPartPassthrough.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.datafixers.types.constant; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.types.templates.TypeTemplate; +import com.mojang.serialization.Codec; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.DynamicOps; + +import java.util.Optional; + +public final class EmptyPartPassthrough extends Type> { + @Override + public String toString() { + return "EmptyPartPassthrough"; + } + + @Override + public Optional> point(final DynamicOps ops) { + return Optional.of(new Dynamic<>(ops)); + } + + @Override + public boolean equals(final Object o, final boolean ignoreRecursionPoints, final boolean checkIndex) { + return this == o; + } + + @Override + public TypeTemplate buildTemplate() { + return DSL.constType(this); + } + + @Override + public Codec> buildCodec() { + return Codec.PASSTHROUGH; + } +} diff --git a/src/main/java/com/mojang/datafixers/types/constant/FloatType.java b/src/main/java/com/mojang/datafixers/types/constant/FloatType.java deleted file mode 100644 index 6f396d84..00000000 --- a/src/main/java/com/mojang/datafixers/types/constant/FloatType.java +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types.constant; - -import com.mojang.datafixers.util.Pair; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.templates.Const; - -import java.util.Optional; - -public final class FloatType extends Const.ConstType { - @Override - public Pair> read(final DynamicOps ops, final T input) { - return ops - .getNumberValue(input) - .map(v -> Pair.of(ops.empty(), Optional.of(v.floatValue()))) - .orElseGet(() -> Pair.of(input, Optional.empty())); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Float value) { - return ops.createFloat(value); - } - - @Override - public String toString() { - return "Float"; - } -} diff --git a/src/main/java/com/mojang/datafixers/types/constant/IntType.java b/src/main/java/com/mojang/datafixers/types/constant/IntType.java deleted file mode 100644 index 50269611..00000000 --- a/src/main/java/com/mojang/datafixers/types/constant/IntType.java +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types.constant; - -import com.mojang.datafixers.util.Pair; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.templates.Const; - -import java.util.Optional; - -public final class IntType extends Const.ConstType { - @Override - public Pair> read(final DynamicOps ops, final T input) { - return ops - .getNumberValue(input) - .map(v -> Pair.of(ops.empty(), Optional.of(v.intValue()))) - .orElseGet(() -> Pair.of(input, Optional.empty())); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Integer value) { - return ops.createInt(value); - } - - @Override - public String toString() { - return "Int"; - } -} diff --git a/src/main/java/com/mojang/datafixers/types/constant/LongType.java b/src/main/java/com/mojang/datafixers/types/constant/LongType.java deleted file mode 100644 index 987f5eaa..00000000 --- a/src/main/java/com/mojang/datafixers/types/constant/LongType.java +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types.constant; - -import com.mojang.datafixers.util.Pair; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.templates.Const; - -import java.util.Optional; - -public final class LongType extends Const.ConstType { - @Override - public Pair> read(final DynamicOps ops, final T input) { - return ops - .getNumberValue(input) - .map(v -> Pair.of(ops.empty(), Optional.of(v.longValue()))) - .orElseGet(() -> Pair.of(input, Optional.empty())); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Long value) { - return ops.createLong(value); - } - - @Override - public String toString() { - return "Long"; - } -} diff --git a/src/main/java/com/mojang/datafixers/types/constant/NamespacedStringType.java b/src/main/java/com/mojang/datafixers/types/constant/NamespacedStringType.java deleted file mode 100644 index 33fe1698..00000000 --- a/src/main/java/com/mojang/datafixers/types/constant/NamespacedStringType.java +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types.constant; - -import com.google.common.base.Function; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.templates.Const; -import com.mojang.datafixers.util.Pair; - -import java.util.Optional; - -public final class NamespacedStringType extends Const.ConstType { - public static Function ENSURE_NAMESPACE = s -> s; - - @Override - public Pair> read(final DynamicOps ops, final T input) { - return ops - .getStringValue(input) - .map(v -> Pair.of(ops.empty(), Optional.of(ENSURE_NAMESPACE.apply(v)))) - .orElseGet(() -> Pair.of(input, Optional.empty())); - } - - @Override - public T write(final DynamicOps ops, final T rest, final String value) { - return ops.createString(value); - } - - @Override - public String toString() { - return "NamespacedString"; - } -} diff --git a/src/main/java/com/mojang/datafixers/types/constant/NilDrop.java b/src/main/java/com/mojang/datafixers/types/constant/NilDrop.java deleted file mode 100644 index 0503270b..00000000 --- a/src/main/java/com/mojang/datafixers/types/constant/NilDrop.java +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types.constant; - -import com.mojang.datafixers.util.Pair; -import com.mojang.datafixers.util.Unit; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.templates.Const; - -import java.util.Optional; - -public final class NilDrop extends Const.ConstType { - @Override - public Pair> read(final DynamicOps ops, final T input) { - return Pair.of(input, point(ops)); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Unit value) { - return rest; - } - - @Override - public String toString() { - return "NilDrop"; - } - - @Override - public Optional point(final DynamicOps ops) { - return Optional.of(Unit.INSTANCE); - } -} diff --git a/src/main/java/com/mojang/datafixers/types/constant/NilSave.java b/src/main/java/com/mojang/datafixers/types/constant/NilSave.java deleted file mode 100644 index 8a8a3198..00000000 --- a/src/main/java/com/mojang/datafixers/types/constant/NilSave.java +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types.constant; - -import com.mojang.datafixers.util.Pair; -import com.mojang.datafixers.Dynamic; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.templates.Const; - -import java.util.Optional; - -public final class NilSave extends Const.ConstType> { - @Override - public Pair>> read(final DynamicOps ops, final T input) { - return Pair.of(ops.empty(), Optional.of(new Dynamic<>(ops, input))); - } - - @SuppressWarnings("unchecked") - @Override - public T write(final DynamicOps ops, final T rest, final Dynamic value) { - return ops.mergeInto(ops.mergeInto(ops.emptyMap(), rest), value.cast(ops)); - } - - @Override - public String toString() { - return "NilSave"; - } - - @Override - public Optional> point(final DynamicOps ops) { - return Optional.of(capEmpty(ops)); - } - - private Dynamic capEmpty(final DynamicOps ops) { - return new Dynamic<>(ops, ops.emptyMap()); - } -} diff --git a/src/main/java/com/mojang/datafixers/types/constant/ShortType.java b/src/main/java/com/mojang/datafixers/types/constant/ShortType.java deleted file mode 100644 index 3557eff1..00000000 --- a/src/main/java/com/mojang/datafixers/types/constant/ShortType.java +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types.constant; - -import com.mojang.datafixers.util.Pair; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.templates.Const; - -import java.util.Optional; - -public final class ShortType extends Const.ConstType { - @Override - public Pair> read(final DynamicOps ops, final T input) { - return ops - .getNumberValue(input) - .map(v -> Pair.of(ops.empty(), Optional.of(v.shortValue()))) - .orElseGet(() -> Pair.of(input, Optional.empty())); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Short value) { - return ops.createShort(value); - } - - @Override - public String toString() { - return "Short"; - } -} diff --git a/src/main/java/com/mojang/datafixers/types/constant/StringType.java b/src/main/java/com/mojang/datafixers/types/constant/StringType.java deleted file mode 100644 index ef855366..00000000 --- a/src/main/java/com/mojang/datafixers/types/constant/StringType.java +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. -package com.mojang.datafixers.types.constant; - -import com.mojang.datafixers.util.Pair; -import com.mojang.datafixers.types.DynamicOps; -import com.mojang.datafixers.types.templates.Const; - -import java.util.Optional; - -public final class StringType extends Const.ConstType { - @Override - public Pair> read(final DynamicOps ops, final T input) { - return ops - .getStringValue(input) - .map(v -> Pair.of(ops.empty(), Optional.of(v))) - .orElseGet(() -> Pair.of(input, Optional.empty())); - } - - @Override - public T write(final DynamicOps ops, final T rest, final String value) { - return ops.createString(value); - } - - @Override - public String toString() { - return "String"; - } -} diff --git a/src/main/java/com/mojang/datafixers/types/templates/Check.java b/src/main/java/com/mojang/datafixers/types/templates/Check.java index a2c76238..9780bc31 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/Check.java +++ b/src/main/java/com/mojang/datafixers/types/templates/Check.java @@ -2,19 +2,21 @@ // Licensed under the MIT license. package com.mojang.datafixers.types.templates; -import com.mojang.datafixers.functions.PointFreeRule; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.DSL; import com.mojang.datafixers.FamilyOptic; import com.mojang.datafixers.RewriteResult; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.TypedOptic; import com.mojang.datafixers.functions.Functions; -import com.mojang.datafixers.types.DynamicOps; +import com.mojang.datafixers.functions.PointFreeRule; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.RecursiveTypeFamily; import com.mojang.datafixers.types.families.TypeFamily; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; import javax.annotation.Nullable; import java.util.Objects; @@ -119,16 +121,18 @@ public CheckType(final String name, final int index, final int expectedIndex, fi } @Override - public Pair> read(final DynamicOps ops, final T input) { - if (index != expectedIndex) { - return Pair.of(input, Optional.empty()); - } - return delegate.read(ops, input); + protected Codec buildCodec() { + return Codec.of( + delegate.codec(), + this::read + ); } - @Override - public T write(final DynamicOps ops, final T rest, final A value) { - return delegate.write(ops, rest, value); + private DataResult> read(final DynamicOps ops, final T input) { + if (index != expectedIndex) { + return DataResult.error("Index mismatch: " + index + " != " + expectedIndex); + } + return delegate.codec().decode(ops, input); } public static RewriteResult fix(final CheckType type, final RewriteResult instance) { diff --git a/src/main/java/com/mojang/datafixers/types/templates/CompoundList.java b/src/main/java/com/mojang/datafixers/types/templates/CompoundList.java index 33bda0e0..a67d421d 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/CompoundList.java +++ b/src/main/java/com/mojang/datafixers/types/templates/CompoundList.java @@ -3,11 +3,8 @@ package com.mojang.datafixers.types.templates; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFixUtils; import com.mojang.datafixers.FamilyOptic; @@ -20,14 +17,16 @@ import com.mojang.datafixers.optics.Optic; import com.mojang.datafixers.optics.Optics; import com.mojang.datafixers.optics.profunctors.TraversalP; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.RecursiveTypeFamily; import com.mojang.datafixers.types.families.TypeFamily; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DynamicOps; import javax.annotation.Nullable; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -173,28 +172,8 @@ public Optional>> point(final DynamicOps ops) { } @Override - public Pair>>> read(final DynamicOps ops, final T input) { - return ops.getMapValues(input).map(map -> { - final ImmutableList.Builder> builder = ImmutableList.builder(); - final ImmutableMap.Builder restBuilder = ImmutableMap.builder(); - for (final Map.Entry entry : map.entrySet()) { - final Pair> keyValue = key.read(ops, entry.getKey()); - final Pair> elementValue = element.read(ops, entry.getValue()); - if (keyValue.getSecond().isPresent() && elementValue.getSecond().isPresent()) { - builder.add(Pair.of(keyValue.getSecond().get(), elementValue.getSecond().get())); - } else { - restBuilder.put(entry); - } - } - return Pair.of(ops.createMap(restBuilder.build()), Optional.of((List>) builder.build())); - }).orElseGet(() -> Pair.of(input, Optional.empty())); - } - - @Override - public T write(final DynamicOps ops, final T rest, final List> value) { - final ImmutableMap.Builder builder = ImmutableMap.builder(); - value.forEach(pair -> builder.put(key.write(ops, ops.empty(), pair.getFirst()), element.write(ops, ops.empty(), pair.getSecond()))); - return ops.merge(rest, ops.createMap(builder.build())); + protected Codec>> buildCodec() { + return Codec.compoundList(key.codec(), element.codec()); } @Override diff --git a/src/main/java/com/mojang/datafixers/types/templates/Const.java b/src/main/java/com/mojang/datafixers/types/templates/Const.java index 2e0d0b75..66db5eae 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/Const.java +++ b/src/main/java/com/mojang/datafixers/types/templates/Const.java @@ -3,7 +3,6 @@ package com.mojang.datafixers.types.templates; import com.google.common.collect.ImmutableSet; -import com.mojang.datafixers.util.Either; import com.mojang.datafixers.DSL; import com.mojang.datafixers.FamilyOptic; import com.mojang.datafixers.OpticParts; @@ -14,6 +13,8 @@ import com.mojang.datafixers.optics.profunctors.Profunctor; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.TypeFamily; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; import javax.annotation.Nullable; import java.util.Objects; @@ -95,7 +96,13 @@ public Type type() { return type; } - public abstract static class ConstType extends Type { + public static final class PrimitiveType extends Type { + private final Codec codec; + + public PrimitiveType(final Codec codec) { + this.codec = codec; + } + @Override public boolean equals(final Object o, final boolean ignoreRecursionPoints, final boolean checkIndex) { return this == o; @@ -105,5 +112,15 @@ public boolean equals(final Object o, final boolean ignoreRecursionPoints, final public TypeTemplate buildTemplate() { return DSL.constType(this); } + + @Override + protected Codec buildCodec() { + return codec; + } + + @Override + public String toString() { + return codec.toString(); + } } } diff --git a/src/main/java/com/mojang/datafixers/types/templates/Hook.java b/src/main/java/com/mojang/datafixers/types/templates/Hook.java index 84db30bb..2dc1bd77 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/Hook.java +++ b/src/main/java/com/mojang/datafixers/types/templates/Hook.java @@ -2,18 +2,21 @@ // Licensed under the MIT license. package com.mojang.datafixers.types.templates; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.DSL; import com.mojang.datafixers.FamilyOptic; import com.mojang.datafixers.RewriteResult; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.TypedOptic; import com.mojang.datafixers.functions.Functions; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.RecursiveTypeFamily; import com.mojang.datafixers.types.families.TypeFamily; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Lifecycle; import javax.annotation.Nullable; import java.util.Objects; @@ -108,13 +111,18 @@ public HookType(final Type delegate, final HookFunction preRead, final HookFu } @Override - public Pair> read(final DynamicOps ops, final T input) { - return delegate.read(ops, preRead.apply(ops, input)); - } - - @Override - public T write(final DynamicOps ops, final T rest, final A value) { - return postWrite.apply(ops, delegate.write(ops, rest, value)); + protected Codec buildCodec() { + return new Codec() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return delegate.codec().decode(ops, preRead.apply(ops, input)).setLifecycle(Lifecycle.experimental()); + } + + @Override + public DataResult encode(final A input, final DynamicOps ops, final T prefix) { + return delegate.codec().encode(input, ops, prefix).map(v -> postWrite.apply(ops, v)).setLifecycle(Lifecycle.experimental()); + } + }; } @Override diff --git a/src/main/java/com/mojang/datafixers/types/templates/List.java b/src/main/java/com/mojang/datafixers/types/templates/List.java index 956bea11..984d852d 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/List.java +++ b/src/main/java/com/mojang/datafixers/types/templates/List.java @@ -5,8 +5,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.DSL; import com.mojang.datafixers.FamilyOptic; import com.mojang.datafixers.OpticParts; @@ -17,17 +15,18 @@ import com.mojang.datafixers.optics.ListTraversal; import com.mojang.datafixers.optics.Optic; import com.mojang.datafixers.optics.profunctors.TraversalP; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.RecursiveTypeFamily; import com.mojang.datafixers.types.families.TypeFamily; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DynamicOps; import javax.annotation.Nullable; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.IntFunction; -import java.util.stream.Collectors; public final class List implements TypeTemplate { private final TypeTemplate element; @@ -157,20 +156,8 @@ public Optional> point(final DynamicOps ops) { } @Override - public Pair>> read(final DynamicOps ops, final T input) { - // TODO: read the same way we read CompoundList? (as much elements as possible) - return ops.getStream(input).map(stream -> { - final java.util.List> list = stream.map(value -> element.read(ops, value).getSecond()).collect(Collectors.toList()); - if (list.stream().anyMatch(o -> !o.isPresent())) { - return Pair.of(input, Optional.>empty()); - } - return Pair.of(ops.empty(), Optional.of(list.stream().map(Optional::get).collect(Collectors.toList()))); - }).orElseGet(() -> Pair.of(input, Optional.empty())); - } - - @Override - public T write(final DynamicOps ops, final T rest, final java.util.List value) { - return ops.merge(rest, ops.createList(value.stream().map(a -> element.write(ops, ops.empty(), a)))); + public Codec> buildCodec() { + return Codec.list(element.codec()); } @Override diff --git a/src/main/java/com/mojang/datafixers/types/templates/Named.java b/src/main/java/com/mojang/datafixers/types/templates/Named.java index 8cb96e16..eee00e4c 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/Named.java +++ b/src/main/java/com/mojang/datafixers/types/templates/Named.java @@ -4,8 +4,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.DSL; import com.mojang.datafixers.FamilyOptic; import com.mojang.datafixers.RewriteResult; @@ -15,15 +13,19 @@ import com.mojang.datafixers.kinds.K1; import com.mojang.datafixers.optics.Optics; import com.mojang.datafixers.optics.profunctors.Cartesian; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.RecursiveTypeFamily; import com.mojang.datafixers.types.families.TypeFamily; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Lifecycle; import javax.annotation.Nullable; import java.util.Objects; import java.util.Optional; -import java.util.function.Function; import java.util.function.IntFunction; public final class Named implements TypeTemplate { @@ -138,16 +140,21 @@ public Optional> findCheckedType(final int index) { } @Override - public Pair>> read(final DynamicOps ops, final T input) { - return element.read(ops, input).mapSecond(vo -> vo.map(v -> Pair.of(name, v))); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Pair value) { - if (!Objects.equals(value.getFirst(), name)) { - throw new IllegalStateException("Named type name doesn't match: expected: " + name + ", got: " + value.getFirst()); - } - return element.write(ops, rest, value.getSecond()); + protected Codec> buildCodec() { + return new Codec>() { + @Override + public DataResult, T>> decode(final DynamicOps ops, final T input) { + return element.codec().decode(ops, input).map(vo -> vo.mapFirst(v -> Pair.of(name, v))).setLifecycle(Lifecycle.experimental()); + } + + @Override + public DataResult encode(final Pair input, final DynamicOps ops, final T prefix) { + if (!Objects.equals(input.getFirst(), name)) { + return DataResult.error("Named type name doesn't match: expected: " + name + ", got: " + input.getFirst(), prefix); + } + return element.codec().encode(input.getSecond(), ops, prefix).setLifecycle(Lifecycle.experimental()); + } + }; } @Override @@ -180,10 +187,6 @@ public int hashCode() { return Objects.hash(name, element); } - public NamedType map(final Function, ? extends Type> function) { - return new NamedType<>(name, function.apply(element)); - } - @Override public Optional> findFieldTypeOpt(final String name) { return element.findFieldTypeOpt(name); diff --git a/src/main/java/com/mojang/datafixers/types/templates/Product.java b/src/main/java/com/mojang/datafixers/types/templates/Product.java index 448e95c8..5f2eb76b 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/Product.java +++ b/src/main/java/com/mojang/datafixers/types/templates/Product.java @@ -4,8 +4,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFixUtils; import com.mojang.datafixers.FamilyOptic; @@ -21,10 +19,13 @@ import com.mojang.datafixers.optics.Optics; import com.mojang.datafixers.optics.Traversal; import com.mojang.datafixers.optics.profunctors.TraversalP; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.RecursiveTypeFamily; import com.mojang.datafixers.types.families.TypeFamily; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DynamicOps; import javax.annotation.Nullable; import java.util.Objects; @@ -100,7 +101,7 @@ private OpticParts cap(final FamilyOptic lo, new Traversal, Pair, A, B>() { @Override public FunctionType, App>> wander(final Applicative applicative, final FunctionType> input) { - return p -> applicative.ap2(Pair::of, + return p -> applicative.ap2(applicative.point(Pair::of), lt.wander(applicative, input).apply(p.getFirst()), rt.wander(applicative, input).apply(p.getSecond()) ); @@ -211,20 +212,8 @@ public Optional> findCheckedType(final int index) { } @Override - public Pair>> read(final DynamicOps ops, final T input) { - final Pair> first = this.first.read(ops, input); - if (first.getSecond().isPresent()) { - final Pair> second = this.second.read(ops, first.getFirst()); - if (second.getSecond().isPresent()) { - return Pair.of(second.getFirst(), Optional.of(Pair.of(first.getSecond().get(), second.getSecond().get()))); - } - } - return Pair.of(input, Optional.empty()); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Pair value) { - return second.write(ops, first.write(ops, rest, value.getFirst()), value.getSecond()); + public Codec> buildCodec() { + return Codec.pair(first.codec(), second.codec()); } @Override diff --git a/src/main/java/com/mojang/datafixers/types/templates/RecursivePoint.java b/src/main/java/com/mojang/datafixers/types/templates/RecursivePoint.java index 2ffadfb9..d20dd60c 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/RecursivePoint.java +++ b/src/main/java/com/mojang/datafixers/types/templates/RecursivePoint.java @@ -2,8 +2,6 @@ // Licensed under the MIT license. package com.mojang.datafixers.types.templates; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.DSL; import com.mojang.datafixers.FamilyOptic; import com.mojang.datafixers.RewriteResult; @@ -12,10 +10,15 @@ import com.mojang.datafixers.View; import com.mojang.datafixers.functions.Functions; import com.mojang.datafixers.functions.PointFreeRule; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.RecursiveTypeFamily; import com.mojang.datafixers.types.families.TypeFamily; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Lifecycle; import org.apache.commons.lang3.ObjectUtils; import javax.annotation.Nullable; @@ -136,14 +139,20 @@ public Type unfold() { return type; } + /** needs to be lazy */ @Override - public Pair> read(final DynamicOps ops, final T input) { - return unfold().read(ops, input); - } + protected Codec buildCodec() { + return new Codec() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return unfold().codec().decode(ops, input).setLifecycle(Lifecycle.experimental()); + } - @Override - public T write(final DynamicOps ops, final T rest, final A value) { - return unfold().write(ops, rest, value); + @Override + public DataResult encode(final A input, final DynamicOps ops, final T prefix) { + return unfold().codec().encode(input, ops, prefix).setLifecycle(Lifecycle.experimental()); + } + }; } @Override diff --git a/src/main/java/com/mojang/datafixers/types/templates/Sum.java b/src/main/java/com/mojang/datafixers/types/templates/Sum.java index 997c81da..bfbf21dd 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/Sum.java +++ b/src/main/java/com/mojang/datafixers/types/templates/Sum.java @@ -4,8 +4,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFixUtils; import com.mojang.datafixers.FamilyOptic; @@ -20,10 +18,12 @@ import com.mojang.datafixers.optics.Optics; import com.mojang.datafixers.optics.Traversal; import com.mojang.datafixers.optics.profunctors.TraversalP; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.RecursiveTypeFamily; import com.mojang.datafixers.types.families.TypeFamily; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DynamicOps; import javax.annotation.Nullable; import java.util.Objects; @@ -211,20 +211,8 @@ public Optional> findCheckedType(final int index) { } @Override - public Pair>> read(final DynamicOps ops, final T input) { - final Pair>> firstRead = first.read(ops, input).mapSecond(vo -> vo.map(Either::left)); - if (firstRead.getSecond().isPresent()) { - return firstRead; - } - return second.read(ops, input).mapSecond(vo -> vo.map(Either::right)); - } - - @Override - public T write(final DynamicOps ops, final T rest, final Either value) { - return value.map( - value1 -> first.write(ops, rest, value1), - value2 -> second.write(ops, rest, value2) - ); + protected Codec> buildCodec() { + return Codec.either(first.codec(), second.codec()); } @Override diff --git a/src/main/java/com/mojang/datafixers/types/templates/Tag.java b/src/main/java/com/mojang/datafixers/types/templates/Tag.java index c13e09ff..cca93412 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/Tag.java +++ b/src/main/java/com/mojang/datafixers/types/templates/Tag.java @@ -2,8 +2,6 @@ // Licensed under the MIT license. package com.mojang.datafixers.types.templates; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.DSL; import com.mojang.datafixers.FamilyOptic; import com.mojang.datafixers.RewriteResult; @@ -11,18 +9,17 @@ import com.mojang.datafixers.TypedOptic; import com.mojang.datafixers.View; import com.mojang.datafixers.functions.Functions; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.RecursiveTypeFamily; import com.mojang.datafixers.types.families.TypeFamily; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DynamicOps; import javax.annotation.Nullable; -import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.function.Function; import java.util.function.IntFunction; -import java.util.stream.Collectors; public final class Tag implements TypeTemplate { private final String name; @@ -172,22 +169,8 @@ public TypeTemplate buildTemplate() { } @Override - public Pair> read(final DynamicOps ops, final T input) { - final Optional> map = ops.getMapValues(input); - final T nameObject = ops.createString(name); - final T elementValue; - if (map.isPresent() && (elementValue = map.get().get(nameObject)) != null) { - final Optional value = element.read(ops, elementValue).getSecond(); - if (value.isPresent()) { - return Pair.of(ops.createMap(map.get().entrySet().stream().filter(e -> !Objects.equals(e.getKey(), nameObject)).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))), value); - } - } - return Pair.of(input, Optional.empty()); - } - - @Override - public T write(final DynamicOps ops, final T rest, final A value) { - return ops.mergeInto(rest, ops.createString(name), element.write(ops, ops.empty(), value)); + protected Codec buildCodec() { + return element.codec().fieldOf(name).codec(); } @Override @@ -212,10 +195,6 @@ public int hashCode() { return Objects.hash(name, element); } - public TagType map(final Function, ? extends Type> function) { - return new TagType<>(name, function.apply(element)); - } - @Override public Optional> findFieldTypeOpt(final String name) { if (Objects.equals(name, this.name)) { diff --git a/src/main/java/com/mojang/datafixers/types/templates/TaggedChoice.java b/src/main/java/com/mojang/datafixers/types/templates/TaggedChoice.java index 847b1332..5dc77c28 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/TaggedChoice.java +++ b/src/main/java/com/mojang/datafixers/types/templates/TaggedChoice.java @@ -6,10 +6,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.DSL; -import com.mojang.datafixers.DataFixerUpper; import com.mojang.datafixers.FamilyOptic; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.RewriteResult; @@ -29,12 +26,16 @@ import com.mojang.datafixers.optics.profunctors.AffineP; import com.mojang.datafixers.optics.profunctors.Cartesian; import com.mojang.datafixers.optics.profunctors.TraversalP; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.RecursiveTypeFamily; import com.mojang.datafixers.types.families.TypeFamily; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Encoder; +import com.mojang.serialization.codecs.KeyDispatchCodec; import javax.annotation.Nullable; import java.util.Arrays; @@ -45,11 +46,8 @@ import java.util.Set; import java.util.function.Function; import java.util.function.IntFunction; -import java.util.stream.Collectors; public final class TaggedChoice implements TypeTemplate { - private static final Logger LOGGER = LogManager.getLogger(); - private final String name; private final Type keyType; private final Map templates; @@ -71,7 +69,7 @@ public int size() { @Override public TypeFamily apply(final TypeFamily family) { return index -> types.computeIfAbsent(Pair.of(family, index), key -> - DSL.taggedChoiceType(name, keyType, templates.entrySet().stream().map(e -> Pair.>of(e.getKey(), e.getValue().apply(key.getFirst()).apply(key.getSecond()))).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond))) + DSL.taggedChoiceType(name, keyType, templates.entrySet().stream().map(e -> Pair.>of(e.getKey(), e.getValue().apply(key.getFirst()).apply(key.getSecond()))).collect(Pair.toMap())) ); } @@ -134,15 +132,13 @@ public TaggedChoiceType(final String name, final Type keyType, final Map, ?> all(final TypeRewriteRule rule, final boolean recurse, final boolean checkIndex) { - final Map> results = types.entrySet().stream().map( - e -> rule.rewrite(e.getValue()).map(v -> Pair.of(e.getKey(), v)) - ).filter( - e -> e.isPresent() && !Objects.equals(e.get().getSecond().view().function(), Functions.id()) - ).map( - Optional::get - ).collect( - Collectors.toMap(Pair::getFirst, Pair::getSecond) - ); + final Map> results = types.entrySet().stream() + .map(e -> rule.rewrite(e.getValue()).map(v -> Pair.of(e.getKey(), v))) + .filter(e -> e.isPresent() && !Objects.equals(e.get().getSecond().view().function(), Functions.id())) + .map(Optional::get) + .collect(Pair.toMap()) + ; + if (results.isEmpty()) { return RewriteResult.nop(this); } else if (results.size() == 1) { @@ -175,55 +171,32 @@ public TaggedChoiceType(final String name, final Type keyType, final Map updateMu(final RecursiveTypeFamily newFamily) { - return DSL.taggedChoiceType(name, keyType, types.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue().updateMu(newFamily))).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond))); + return DSL.taggedChoiceType(name, keyType, types.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue().updateMu(newFamily))).collect(Pair.toMap())); } @Override public TypeTemplate buildTemplate() { - return DSL.taggedChoice(name, keyType, types.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue().template())).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond))); + return DSL.taggedChoice(name, keyType, types.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue().template())).collect(Pair.toMap())); } - @Override - public Pair>> read(final DynamicOps ops, final T input) { - final Optional> values = ops.getMapValues(input); - if (values.isPresent()) { - final Map map = values.get(); - final T nameObject = ops.createString(name); - final T mapValue = map.get(nameObject); - if (mapValue != null) { - final Optional key = keyType.read(ops, mapValue).getSecond(); - //noinspection OptionalIsPresent - final K keyValue = key.isPresent() ? key.get() : null; - final Type type = keyValue != null ? types.get(keyValue) : null; - if (type == null) { - if (DataFixerUpper.ERRORS_ARE_FATAL) { - throw new IllegalArgumentException("Unsupported key: " + keyValue + " in " + this); - } else { - LOGGER.warn("Unsupported key: {} in {}", keyValue, this); - return Pair.of(input, Optional.empty()); - } - } - - return type.read(ops, input).mapSecond(vo -> vo.map(v -> Pair.of(keyValue, v))); - } - } - return Pair.of(input, Optional.empty()); + @SuppressWarnings("unchecked") + private DataResult>> encoder(final Pair pair) { + return getCodec(pair.getFirst()).map(c -> ((Encoder) c).comap(p -> (V) p.getSecond())); } @Override - public T write(final DynamicOps ops, final T rest, final Pair value) { - final Type type = types.get(value.getFirst()); - if (type == null) { - // TODO: better error handling? - // TODO: See todo in read method - throw new IllegalArgumentException("Unsupported key: " + value.getFirst() + " in " + this); - } - return capWrite(ops, type, value.getFirst(), value.getSecond(), rest); + protected Codec> buildCodec() { + return new KeyDispatchCodec>( + name, + keyType.codec(), + p -> DataResult.success(p.getFirst()), + k -> getCodec(k).map(c -> c.map(v -> Pair.of(k, v))), + this::encoder + ).codec(); } - @SuppressWarnings("unchecked") - private T capWrite(final DynamicOps ops, final Type type, final K key, final Object value, final T rest) { - return ops.mergeInto(type.write(ops, rest, (A) value), ops.createString(name), keyType.write(ops, ops.empty(), key)); + private DataResult> getCodec(final K k) { + return Optional.ofNullable(types.get(k)).map(t -> DataResult.success(t.codec())).orElseGet(() -> DataResult.error("Unsupported key: " + k)); } @Override @@ -245,13 +218,12 @@ public Optional> findFieldTypeOpt(final String name) { @Override public Either, ?, FT, FR>, FieldNotFoundException> findTypeInChildren(final Type type, final Type resultType, final TypeMatcher matcher, final boolean recurse) { - final Map> optics = types.entrySet().stream().map( - e -> Pair.of(e.getKey(), e.getValue().findType(type, resultType, matcher, recurse)) - ).filter( - e -> e.getSecond().left().isPresent() - ).map( - e -> e.mapSecond(o -> o.left().get()) - ).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)); + final Map> optics = types.entrySet().stream() + .map(e -> Pair.of(e.getKey(), e.getValue().findType(type, resultType, matcher, recurse))) + .filter(e -> e.getSecond().left().isPresent()) + .map(e -> e.mapSecond(o -> o.left().get())) + .collect(Pair.toMap()) + ; if (optics.isEmpty()) { return Either.right(new FieldNotFoundException("Not found in any choices")); @@ -350,7 +322,7 @@ private FT capView(final Pair s, final TypedOptic opt throw new IllegalStateException("Could not merge TaggedChoiceType optics, unknown bound: " + Arrays.toString(bounds.toArray())); } - final Map> newTypes = types.entrySet().stream().map(e -> Pair.of(e.getKey(), optics.containsKey(e.getKey()) ? optics.get(e.getKey()).tType() : e.getValue())).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)); + final Map> newTypes = types.entrySet().stream().map(e -> Pair.of(e.getKey(), optics.containsKey(e.getKey()) ? optics.get(e.getKey()).tType() : e.getValue())).collect(Pair.toMap()); return Either.left(new TypedOptic<>( bound, @@ -385,7 +357,7 @@ public boolean equals(final Object obj, final boolean ignoreRecursionPoints, fin if (this == obj) { return true; } - if (!(obj instanceof TaggedChoice.TaggedChoiceType)) { + if (!(obj instanceof TaggedChoiceType)) { return false; } final TaggedChoiceType other = (TaggedChoiceType) obj; diff --git a/src/main/java/com/mojang/datafixers/types/templates/TypeTemplate.java b/src/main/java/com/mojang/datafixers/types/templates/TypeTemplate.java index f6e2f3a5..38236e98 100644 --- a/src/main/java/com/mojang/datafixers/types/templates/TypeTemplate.java +++ b/src/main/java/com/mojang/datafixers/types/templates/TypeTemplate.java @@ -2,12 +2,12 @@ // Licensed under the MIT license. package com.mojang.datafixers.types.templates; -import com.mojang.datafixers.util.Either; import com.mojang.datafixers.DSL; import com.mojang.datafixers.FamilyOptic; import com.mojang.datafixers.RewriteResult; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.TypeFamily; +import com.mojang.datafixers.util.Either; import javax.annotation.Nullable; import java.util.function.IntFunction; @@ -21,7 +21,7 @@ default Type toSimpleType() { return apply(new TypeFamily() { @Override public Type apply(final int index) { - return DSL.nilType(); + return DSL.emptyPartType(); } /*@Override diff --git a/src/main/java/com/mojang/datafixers/util/Function10.java b/src/main/java/com/mojang/datafixers/util/Function10.java new file mode 100644 index 00000000..a30c9ec7 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function10.java @@ -0,0 +1,44 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function10 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5, t6, t7, t8, t9, t10) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5, t6, t7, t8, t9, t10) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5, t6, t7, t8, t9, t10) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5, t6, t7, t8, t9, t10) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); + } + + default Function5> curry5() { + return (t1, t2, t3, t4, t5) -> (t6, t7, t8, t9, t10) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); + } + + default Function6> curry6() { + return (t1, t2, t3, t4, t5, t6) -> (t7, t8, t9, t10) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); + } + + default Function7> curry7() { + return (t1, t2, t3, t4, t5, t6, t7) -> (t8, t9, t10) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); + } + + default Function8> curry8() { + return (t1, t2, t3, t4, t5, t6, t7, t8) -> (t9, t10) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); + } + + default Function9> curry9() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9) -> t10 -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function11.java b/src/main/java/com/mojang/datafixers/util/Function11.java new file mode 100644 index 00000000..65e49904 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function11.java @@ -0,0 +1,48 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function11 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5, t6, t7, t8, t9, t10, t11) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5, t6, t7, t8, t9, t10, t11) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5, t6, t7, t8, t9, t10, t11) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + default Function5> curry5() { + return (t1, t2, t3, t4, t5) -> (t6, t7, t8, t9, t10, t11) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + default Function6> curry6() { + return (t1, t2, t3, t4, t5, t6) -> (t7, t8, t9, t10, t11) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + default Function7> curry7() { + return (t1, t2, t3, t4, t5, t6, t7) -> (t8, t9, t10, t11) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + default Function8> curry8() { + return (t1, t2, t3, t4, t5, t6, t7, t8) -> (t9, t10, t11) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + default Function9> curry9() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9) -> (t10, t11) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + default Function10> curry10() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) -> t11 -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function12.java b/src/main/java/com/mojang/datafixers/util/Function12.java new file mode 100644 index 00000000..4eead84a --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function12.java @@ -0,0 +1,52 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function12 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5, t6, t7, t8, t9, t10, t11, t12) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5, t6, t7, t8, t9, t10, t11, t12) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + + default Function5> curry5() { + return (t1, t2, t3, t4, t5) -> (t6, t7, t8, t9, t10, t11, t12) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + + default Function6> curry6() { + return (t1, t2, t3, t4, t5, t6) -> (t7, t8, t9, t10, t11, t12) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + + default Function7> curry7() { + return (t1, t2, t3, t4, t5, t6, t7) -> (t8, t9, t10, t11, t12) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + + default Function8> curry8() { + return (t1, t2, t3, t4, t5, t6, t7, t8) -> (t9, t10, t11, t12) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + + default Function9> curry9() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9) -> (t10, t11, t12) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + + default Function10> curry10() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) -> (t11, t12) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + + default Function11> curry11() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) -> t12 -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function13.java b/src/main/java/com/mojang/datafixers/util/Function13.java new file mode 100644 index 00000000..cac973b2 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function13.java @@ -0,0 +1,56 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function13 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5, t6, t7, t8, t9, t10, t11, t12, t13) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default Function5> curry5() { + return (t1, t2, t3, t4, t5) -> (t6, t7, t8, t9, t10, t11, t12, t13) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default Function6> curry6() { + return (t1, t2, t3, t4, t5, t6) -> (t7, t8, t9, t10, t11, t12, t13) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default Function7> curry7() { + return (t1, t2, t3, t4, t5, t6, t7) -> (t8, t9, t10, t11, t12, t13) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default Function8> curry8() { + return (t1, t2, t3, t4, t5, t6, t7, t8) -> (t9, t10, t11, t12, t13) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default Function9> curry9() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9) -> (t10, t11, t12, t13) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default Function10> curry10() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) -> (t11, t12, t13) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default Function11> curry11() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) -> (t12, t13) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default Function12> curry12() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) -> t13 -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function14.java b/src/main/java/com/mojang/datafixers/util/Function14.java new file mode 100644 index 00000000..5cc5d1a1 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function14.java @@ -0,0 +1,60 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function14 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Function5> curry5() { + return (t1, t2, t3, t4, t5) -> (t6, t7, t8, t9, t10, t11, t12, t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Function6> curry6() { + return (t1, t2, t3, t4, t5, t6) -> (t7, t8, t9, t10, t11, t12, t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Function7> curry7() { + return (t1, t2, t3, t4, t5, t6, t7) -> (t8, t9, t10, t11, t12, t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Function8> curry8() { + return (t1, t2, t3, t4, t5, t6, t7, t8) -> (t9, t10, t11, t12, t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Function9> curry9() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9) -> (t10, t11, t12, t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Function10> curry10() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) -> (t11, t12, t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Function11> curry11() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) -> (t12, t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Function12> curry12() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) -> (t13, t14) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Function13> curry13() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) -> t14 -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function15.java b/src/main/java/com/mojang/datafixers/util/Function15.java new file mode 100644 index 00000000..6540da51 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function15.java @@ -0,0 +1,64 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function15 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14, T15 t15); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function5> curry5() { + return (t1, t2, t3, t4, t5) -> (t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function6> curry6() { + return (t1, t2, t3, t4, t5, t6) -> (t7, t8, t9, t10, t11, t12, t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function7> curry7() { + return (t1, t2, t3, t4, t5, t6, t7) -> (t8, t9, t10, t11, t12, t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function8> curry8() { + return (t1, t2, t3, t4, t5, t6, t7, t8) -> (t9, t10, t11, t12, t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function9> curry9() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9) -> (t10, t11, t12, t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function10> curry10() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) -> (t11, t12, t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function11> curry11() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) -> (t12, t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function12> curry12() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) -> (t13, t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function13> curry13() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) -> (t14, t15) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Function14> curry14() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) -> t15 -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function16.java b/src/main/java/com/mojang/datafixers/util/Function16.java new file mode 100644 index 00000000..0b80b904 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function16.java @@ -0,0 +1,68 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function16 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10, T11 t11, T12 t12, T13 t13, T14 t14, T15 t15, T16 t16); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function5> curry5() { + return (t1, t2, t3, t4, t5) -> (t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function6> curry6() { + return (t1, t2, t3, t4, t5, t6) -> (t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function7> curry7() { + return (t1, t2, t3, t4, t5, t6, t7) -> (t8, t9, t10, t11, t12, t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function8> curry8() { + return (t1, t2, t3, t4, t5, t6, t7, t8) -> (t9, t10, t11, t12, t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function9> curry9() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9) -> (t10, t11, t12, t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function10> curry10() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) -> (t11, t12, t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function11> curry11() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) -> (t12, t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function12> curry12() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) -> (t13, t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function13> curry13() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) -> (t14, t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function14> curry14() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) -> (t15, t16) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } + + default Function15> curry15() { + return (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) -> t16 -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function3.java b/src/main/java/com/mojang/datafixers/util/Function3.java new file mode 100644 index 00000000..1e3fe820 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function3.java @@ -0,0 +1,16 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function3 { + R apply(T1 t1, T2 t2, T3 t3); + + default Function> curry() { + return t1 -> (t2, t3) -> apply(t1, t2, t3); + } + + default BiFunction> curry2() { + return (t1, t2) -> t3 -> apply(t1, t2, t3); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function4.java b/src/main/java/com/mojang/datafixers/util/Function4.java new file mode 100644 index 00000000..c33e4d89 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function4.java @@ -0,0 +1,20 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function4 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4); + + default Function> curry() { + return t1 -> (t2, t3, t4) -> apply(t1, t2, t3, t4); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4) -> apply(t1, t2, t3, t4); + } + + default Function3> curry3() { + return (t1, t2, t3) -> t4 -> apply(t1, t2, t3, t4); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function5.java b/src/main/java/com/mojang/datafixers/util/Function5.java new file mode 100644 index 00000000..87112d23 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function5.java @@ -0,0 +1,24 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function5 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5) -> apply(t1, t2, t3, t4, t5); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5) -> apply(t1, t2, t3, t4, t5); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5) -> apply(t1, t2, t3, t4, t5); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5) -> apply(t1, t2, t3, t4, t5); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function6.java b/src/main/java/com/mojang/datafixers/util/Function6.java new file mode 100644 index 00000000..37a7366d --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function6.java @@ -0,0 +1,28 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function6 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5, t6) -> apply(t1, t2, t3, t4, t5, t6); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5, t6) -> apply(t1, t2, t3, t4, t5, t6); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5, t6) -> apply(t1, t2, t3, t4, t5, t6); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5, t6) -> apply(t1, t2, t3, t4, t5, t6); + } + + default Function5> curry5() { + return (t1, t2, t3, t4, t5) -> t6 -> apply(t1, t2, t3, t4, t5, t6); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function7.java b/src/main/java/com/mojang/datafixers/util/Function7.java new file mode 100644 index 00000000..b2aaa167 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function7.java @@ -0,0 +1,32 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function7 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5, t6, t7) -> apply(t1, t2, t3, t4, t5, t6, t7); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5, t6, t7) -> apply(t1, t2, t3, t4, t5, t6, t7); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5, t6, t7) -> apply(t1, t2, t3, t4, t5, t6, t7); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5, t6, t7) -> apply(t1, t2, t3, t4, t5, t6, t7); + } + + default Function5> curry5() { + return (t1, t2, t3, t4, t5) -> (t6, t7) -> apply(t1, t2, t3, t4, t5, t6, t7); + } + + default Function6> curry6() { + return (t1, t2, t3, t4, t5, t6) -> t7 -> apply(t1, t2, t3, t4, t5, t6, t7); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function8.java b/src/main/java/com/mojang/datafixers/util/Function8.java new file mode 100644 index 00000000..859e43c6 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function8.java @@ -0,0 +1,36 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function8 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5, t6, t7, t8) -> apply(t1, t2, t3, t4, t5, t6, t7, t8); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5, t6, t7, t8) -> apply(t1, t2, t3, t4, t5, t6, t7, t8); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5, t6, t7, t8) -> apply(t1, t2, t3, t4, t5, t6, t7, t8); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5, t6, t7, t8) -> apply(t1, t2, t3, t4, t5, t6, t7, t8); + } + + default Function5> curry5() { + return (t1, t2, t3, t4, t5) -> (t6, t7, t8) -> apply(t1, t2, t3, t4, t5, t6, t7, t8); + } + + default Function6> curry6() { + return (t1, t2, t3, t4, t5, t6) -> (t7, t8) -> apply(t1, t2, t3, t4, t5, t6, t7, t8); + } + + default Function7> curry7() { + return (t1, t2, t3, t4, t5, t6, t7) -> t8 -> apply(t1, t2, t3, t4, t5, t6, t7, t8); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Function9.java b/src/main/java/com/mojang/datafixers/util/Function9.java new file mode 100644 index 00000000..9deb3961 --- /dev/null +++ b/src/main/java/com/mojang/datafixers/util/Function9.java @@ -0,0 +1,40 @@ +package com.mojang.datafixers.util; + +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface Function9 { + R apply(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9); + + default Function> curry() { + return t1 -> (t2, t3, t4, t5, t6, t7, t8, t9) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9); + } + + default BiFunction> curry2() { + return (t1, t2) -> (t3, t4, t5, t6, t7, t8, t9) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9); + } + + default Function3> curry3() { + return (t1, t2, t3) -> (t4, t5, t6, t7, t8, t9) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9); + } + + default Function4> curry4() { + return (t1, t2, t3, t4) -> (t5, t6, t7, t8, t9) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9); + } + + default Function5> curry5() { + return (t1, t2, t3, t4, t5) -> (t6, t7, t8, t9) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9); + } + + default Function6> curry6() { + return (t1, t2, t3, t4, t5, t6) -> (t7, t8, t9) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9); + } + + default Function7> curry7() { + return (t1, t2, t3, t4, t5, t6, t7) -> (t8, t9) -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9); + } + + default Function8> curry8() { + return (t1, t2, t3, t4, t5, t6, t7, t8) -> t9 -> apply(t1, t2, t3, t4, t5, t6, t7, t8, t9); + } +} diff --git a/src/main/java/com/mojang/datafixers/util/Pair.java b/src/main/java/com/mojang/datafixers/util/Pair.java index b6677aeb..dfa0280e 100644 --- a/src/main/java/com/mojang/datafixers/util/Pair.java +++ b/src/main/java/com/mojang/datafixers/util/Pair.java @@ -8,8 +8,11 @@ import com.mojang.datafixers.kinds.K1; import com.mojang.datafixers.kinds.Traversable; +import java.util.Map; import java.util.Objects; import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; public class Pair implements App, F> { public static final class Mu implements K1 {} @@ -69,6 +72,10 @@ public static Pair of(final F first, final S second) { return new Pair<>(first, second); } + public static Collector, ?, Map> toMap() { + return Collectors.toMap(Pair::getFirst, Pair::getSecond); + } + public static final class Instance implements Traversable, Instance.Mu>, CartesianLike, S2, Instance.Mu> { public static final class Mu implements Traversable.Mu, CartesianLike.Mu {} diff --git a/src/main/java/com/mojang/serialization/Codec.java b/src/main/java/com/mojang/serialization/Codec.java new file mode 100644 index 00000000..e7f5d46f --- /dev/null +++ b/src/main/java/com/mojang/serialization/Codec.java @@ -0,0 +1,621 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.mojang.datafixers.DataFixUtils; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.datafixers.util.Unit; +import com.mojang.serialization.codecs.CompoundListCodec; +import com.mojang.serialization.codecs.EitherCodec; +import com.mojang.serialization.codecs.EitherMapCodec; +import com.mojang.serialization.codecs.KeyDispatchCodec; +import com.mojang.serialization.codecs.ListCodec; +import com.mojang.serialization.codecs.OptionalFieldCodec; +import com.mojang.serialization.codecs.PairCodec; +import com.mojang.serialization.codecs.PairMapCodec; +import com.mojang.serialization.codecs.PrimitiveCodec; +import com.mojang.serialization.codecs.SimpleMapCodec; +import com.mojang.serialization.codecs.UnboundedMapCodec; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +public interface Codec extends Encoder, Decoder { + @Override + default Codec withLifecycle(final Lifecycle lifecycle) { + return new Codec() { + @Override + public DataResult encode(final A input, final DynamicOps ops, final T prefix) { + return Codec.this.encode(input, ops, prefix).setLifecycle(lifecycle); + } + + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return Codec.this.decode(ops, input).setLifecycle(lifecycle); + } + + @Override + public String toString() { + return Codec.this.toString(); + } + }; + } + + default Codec stable() { + return withLifecycle(Lifecycle.stable()); + } + + default Codec deprecated(final int since) { + return withLifecycle(Lifecycle.deprecated(since)); + } + + static Codec of(final Encoder encoder, final Decoder decoder) { + return of(encoder, decoder, "Codec[" + encoder + " " + decoder + "]"); + } + + static Codec of(final Encoder encoder, final Decoder decoder, final String name) { + return new Codec() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return decoder.decode(ops, input); + } + + @Override + public DataResult encode(final A input, final DynamicOps ops, final T prefix) { + return encoder.encode(input, ops, prefix); + } + + @Override + public String toString() { + return name; + } + }; + } + + static MapCodec of(final MapEncoder encoder, final MapDecoder decoder) { + return of(encoder, decoder, "MapCodec[" + encoder + " " + decoder + "]"); + } + + static MapCodec of(final MapEncoder encoder, final MapDecoder decoder, final String name) { + return new MapCodec() { + @Override + public Stream keys(final DynamicOps ops) { + return Stream.concat(encoder.keys(ops), decoder.keys(ops)); + } + + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return decoder.decode(ops, input); + } + + @Override + public RecordBuilder encode(final A input, final DynamicOps ops, final RecordBuilder prefix) { + return encoder.encode(input, ops, prefix); + } + + @Override + public String toString() { + return name; + } + }; + } + + static Codec> pair(final Codec first, final Codec second) { + return new PairCodec<>(first, second); + } + + static Codec> either(final Codec first, final Codec second) { + return new EitherCodec<>(first, second); + } + + static MapCodec> mapPair(final MapCodec first, final MapCodec second) { + return new PairMapCodec<>(first, second); + } + + static MapCodec> mapEither(final MapCodec first, final MapCodec second) { + return new EitherMapCodec<>(first, second); + } + + static Codec> list(final Codec elementCodec) { + return new ListCodec<>(elementCodec); + } + + static Codec>> compoundList(final Codec keyCodec, final Codec elementCodec) { + return new CompoundListCodec<>(keyCodec, elementCodec); + } + + static SimpleMapCodec simpleMap(final Codec keyCodec, final Codec elementCodec, final Keyable keys) { + return new SimpleMapCodec<>(keyCodec, elementCodec, keys); + } + + static UnboundedMapCodec unboundedMap(final Codec keyCodec, final Codec elementCodec) { + return new UnboundedMapCodec<>(keyCodec, elementCodec); + } + + static MapCodec> optionalField(final String name, final Codec elementCodec) { + return new OptionalFieldCodec<>(name, elementCodec); + } + + default Codec> listOf() { + return list(this); + } + + default Codec xmap(final Function to, final Function from) { + return Codec.of(comap(from), map(to), toString() + "[xmapped]"); + } + + default Codec comapFlatMap(final Function> to, final Function from) { + return Codec.of(comap(from), flatMap(to), toString() + "[comapFlatMapped]"); + } + + default Codec flatComapMap(final Function to, final Function> from) { + return Codec.of(flatComap(from), map(to), toString() + "[flatComapMapped]"); + } + + default Codec flatXmap(final Function> to, final Function> from) { + return Codec.of(flatComap(from), flatMap(to), toString() + "[flatXmapped]"); + } + + @Override + default MapCodec fieldOf(final String name) { + return MapCodec.of( + Encoder.super.fieldOf(name), + Decoder.super.fieldOf(name), + "Field[" + name + ": " + toString() + "]" + ); + } + + default MapCodec> optionalFieldOf(final String name) { + return optionalField(name, this); + } + + default MapCodec optionalFieldOf(final String name, final A defaultValue) { + return optionalField(name, this).xmap( + o -> o.orElse(defaultValue), + a -> Objects.equals(a, defaultValue) ? Optional.empty() : Optional.of(a) + ); + } + + default MapCodec optionalFieldOf(final String name, final A defaultValue, final Lifecycle lifecycleOfDefault) { + return optionalFieldOf(name, Lifecycle.experimental(), defaultValue, lifecycleOfDefault); + } + + default MapCodec optionalFieldOf(final String name, final Lifecycle fieldLifecycle, final A defaultValue, final Lifecycle lifecycleOfDefault) { + // setting lifecycle to stable on the outside since it will be overriden by the passed parameters + return optionalField(name, this).stable().flatXmap( + o -> o.map(v -> DataResult.success(v, fieldLifecycle)).orElse(DataResult.success(defaultValue, lifecycleOfDefault)), + a -> Objects.equals(a, defaultValue) ? DataResult.success(Optional.empty(), lifecycleOfDefault) : DataResult.success(Optional.of(a), fieldLifecycle) + ); + } + + interface ResultFunction { + DataResult> apply(final DynamicOps ops, final T input, final DataResult> a); + + DataResult coApply(final DynamicOps ops, final A input, final DataResult t); + } + + default Codec mapResult(final ResultFunction function) { + return new Codec() { + @Override + public DataResult encode(final A input, final DynamicOps ops, final T prefix) { + return function.coApply(ops, input, Codec.this.encode(input, ops, prefix)); + } + + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return function.apply(ops, input, Codec.this.decode(ops, input)); + } + + @Override + public String toString() { + return Codec.this + "[mapResult " + function + "]"; + } + }; + } + + default Codec withDefault(final Consumer onError, final A value) { + return withDefault(DataFixUtils.consumerToFunction(onError), value); + } + + default Codec withDefault(final UnaryOperator onError, final A value) { + return mapResult(new ResultFunction() { + @Override + public DataResult> apply(final DynamicOps ops, final T input, final DataResult> a) { + return DataResult.success(a.mapError(onError).result().orElseGet(() -> Pair.of(value, input))); + } + + @Override + public DataResult coApply(final DynamicOps ops, final A input, final DataResult t) { + return t.mapError(onError); + } + + @Override + public String toString() { + return "WithDefault[" + onError + " " + value + "]"; + } + }); + } + + default Codec withDefault(final Consumer onError, final Supplier value) { + return withDefault(DataFixUtils.consumerToFunction(onError), value); + } + + default Codec withDefault(final UnaryOperator onError, final Supplier value) { + return mapResult(new ResultFunction() { + @Override + public DataResult> apply(final DynamicOps ops, final T input, final DataResult> a) { + return DataResult.success(a.mapError(onError).result().orElseGet(() -> Pair.of(value.get(), input))); + } + + @Override + public DataResult coApply(final DynamicOps ops, final A input, final DataResult t) { + return t.mapError(onError); + } + + @Override + public String toString() { + return "WithDefault[" + onError + " " + value.get() + "]"; + } + }); + } + + default Codec withDefault(final A value) { + return mapResult(new ResultFunction() { + @Override + public DataResult> apply(final DynamicOps ops, final T input, final DataResult> a) { + return DataResult.success(a.result().orElseGet(() -> Pair.of(value, input))); + } + + @Override + public DataResult coApply(final DynamicOps ops, final A input, final DataResult t) { + return t; + } + + @Override + public String toString() { + return "WithDefault[" + value + "]"; + } + }); + } + + default Codec withDefault(final Supplier value) { + return mapResult(new ResultFunction() { + @Override + public DataResult> apply(final DynamicOps ops, final T input, final DataResult> a) { + return DataResult.success(a.result().orElseGet(() -> Pair.of(value.get(), input))); + } + + @Override + public DataResult coApply(final DynamicOps ops, final A input, final DataResult t) { + return t; + } + + @Override + public String toString() { + return "WithDefault[" + value.get() + "]"; + } + }); + } + + @Override + default Codec promotePartial(final Consumer onError) { + return Codec.of(this, Decoder.super.promotePartial(onError)); + } + + static Codec unit(final A defaultValue) { + return unit(() -> defaultValue); + } + + static Codec unit(final Supplier defaultValue) { + return MapCodec.unit(defaultValue).codec(); + } + + default Codec dispatch(final Function type, final Function> codec) { + return dispatch("type", type, codec); + } + + default Codec dispatch(final String typeKey, final Function type, final Function> codec) { + return partialDispatch(typeKey, type.andThen(DataResult::success), codec.andThen(DataResult::success)); + } + + default Codec dispatchStable(final Function type, final Function> codec) { + return dispatchStable("type", type, codec); + } + + default Codec dispatchStable(final String typeKey, final Function type, final Function> codec) { + return partialDispatch(typeKey, e -> DataResult.success(type.apply(e), Lifecycle.stable()), a -> DataResult.success(codec.apply(a), Lifecycle.stable())); + } + + default Codec dispatchDeprecated(final int since, final Function type, final Function> codec) { + return dispatchDeprecated(since, "type", type, codec); + } + + default Codec dispatchDeprecated(final int since, final String typeKey, final Function type, final Function> codec) { + final Lifecycle deprecated = Lifecycle.deprecated(since); + return partialDispatch(typeKey, e -> DataResult.success(type.apply(e), deprecated), a -> DataResult.success(codec.apply(a), deprecated)); + } + + default Codec partialDispatch(final String typeKey, final Function> type, final Function>> codec) { + return new KeyDispatchCodec<>(typeKey, this, type, codec).codec(); + } + + default MapCodec dispatchMap(final Function type, final Function> codec) { + return dispatchMap("type", type, codec); + } + + default MapCodec dispatchMap(final String typeKey, final Function type, final Function> codec) { + return partialDispatchMap(typeKey, type.andThen(DataResult::success), codec.andThen(DataResult::success)); + } + + default MapCodec dispatchStableMap(final Function type, final Function> codec) { + return dispatchStableMap("type", type, codec); + } + + default MapCodec dispatchStableMap(final String typeKey, final Function type, final Function> codec) { + return partialDispatchMap(typeKey, e -> DataResult.success(type.apply(e), Lifecycle.stable()), a -> DataResult.success(codec.apply(a), Lifecycle.stable())); + } + + default MapCodec dispatchDeprecatedMap(final int since, final Function type, final Function> codec) { + return dispatchDeprecatedMap(since, "type", type, codec); + } + + default MapCodec dispatchDeprecatedMap(final int since, final String typeKey, final Function type, final Function> codec) { + final Lifecycle deprecated = Lifecycle.deprecated(since); + return partialDispatchMap(typeKey, e -> DataResult.success(type.apply(e), deprecated), a -> DataResult.success(codec.apply(a), deprecated)); + } + + default MapCodec partialDispatchMap(final String typeKey, final Function> type, final Function>> codec) { + return new KeyDispatchCodec<>(typeKey, this, type, codec); + } + + PrimitiveCodec BOOL = new PrimitiveCodec() { + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getBooleanValue(input); + } + + @Override + public T write(final DynamicOps ops, final Boolean value) { + return ops.createBoolean(value); + } + + @Override + public String toString() { + return "Bool"; + } + }; + + PrimitiveCodec BYTE = new PrimitiveCodec() { + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getNumberValue(input) + .map(Number::byteValue); + } + + @Override + public T write(final DynamicOps ops, final Byte value) { + return ops.createByte(value); + } + + @Override + public String toString() { + return "Byte"; + } + }; + + PrimitiveCodec SHORT = new PrimitiveCodec() { + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getNumberValue(input) + .map(Number::shortValue); + } + + @Override + public T write(final DynamicOps ops, final Short value) { + return ops.createShort(value); + } + + @Override + public String toString() { + return "Short"; + } + }; + + PrimitiveCodec INT = new PrimitiveCodec() { + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getNumberValue(input) + .map(Number::intValue); + } + + @Override + public T write(final DynamicOps ops, final Integer value) { + return ops.createInt(value); + } + + @Override + public String toString() { + return "Int"; + } + }; + + PrimitiveCodec LONG = new PrimitiveCodec() { + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getNumberValue(input) + .map(Number::longValue); + } + + @Override + public T write(final DynamicOps ops, final Long value) { + return ops.createLong(value); + } + + @Override + public String toString() { + return "Long"; + } + }; + + PrimitiveCodec FLOAT = new PrimitiveCodec() { + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getNumberValue(input) + .map(Number::floatValue); + } + + @Override + public T write(final DynamicOps ops, final Float value) { + return ops.createFloat(value); + } + + @Override + public String toString() { + return "Float"; + } + }; + + PrimitiveCodec DOUBLE = new PrimitiveCodec() { + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getNumberValue(input) + .map(Number::doubleValue); + } + + @Override + public T write(final DynamicOps ops, final Double value) { + return ops.createDouble(value); + } + + @Override + public String toString() { + return "Double"; + } + }; + + PrimitiveCodec STRING = new PrimitiveCodec() { + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getStringValue(input); + } + + @Override + public T write(final DynamicOps ops, final String value) { + return ops.createString(value); + } + + @Override + public String toString() { + return "String"; + } + }; + + PrimitiveCodec BYTE_BUFFER = new PrimitiveCodec() { + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getByteBuffer(input); + } + + @Override + public T write(final DynamicOps ops, final ByteBuffer value) { + return ops.createByteList(value); + } + + @Override + public String toString() { + return "ByteBuffer"; + } + }; + + PrimitiveCodec INT_STREAM = new PrimitiveCodec() { + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getIntStream(input); + } + + @Override + public T write(final DynamicOps ops, final IntStream value) { + return ops.createIntList(value); + } + + @Override + public String toString() { + return "IntStream"; + } + }; + + PrimitiveCodec LONG_STREAM = new PrimitiveCodec() { + @Override + public DataResult read(final DynamicOps ops, final T input) { + return ops + .getLongStream(input); + } + + @Override + public T write(final DynamicOps ops, final LongStream value) { + return ops.createLongList(value); + } + + @Override + public String toString() { + return "LongStream"; + } + }; + + Codec> PASSTHROUGH = new Codec>() { + @Override + public DataResult, T>> decode(final DynamicOps ops, final T input) { + return DataResult.success(Pair.of(new Dynamic<>(ops, input), ops.empty())); + } + + @Override + public DataResult encode(final Dynamic input, final DynamicOps ops, final T prefix) { + if (input.getValue() == input.getOps().empty()) { + // nothing to merge, return rest + return DataResult.success(prefix, Lifecycle.experimental()); + } + + final T casted = input.convert(ops).getValue(); + if (prefix == ops.empty()) { + // no need to merge anything, return the old value + return DataResult.success(casted, Lifecycle.experimental()); + } + + final DataResult toMap = ops.getMap(casted).flatMap(map -> ops.mergeToMap(prefix, map)); + return toMap.result().map(DataResult::success).orElseGet(() -> { + final DataResult toList = ops.getStream(casted).flatMap(stream -> ops.mergeToList(prefix, stream.collect(Collectors.toList()))); + return toList.result().map(DataResult::success).orElseGet(() -> + DataResult.error("Don't know how to merge " + prefix + " and " + casted, prefix, Lifecycle.experimental()) + ); + }); + } + + @Override + public String toString() { + return "passthrough"; + } + }; + + MapCodec EMPTY = MapCodec.of(Encoder.empty(), Decoder.unit(Unit.INSTANCE)); +} diff --git a/src/main/java/com/mojang/serialization/Compressable.java b/src/main/java/com/mojang/serialization/Compressable.java new file mode 100644 index 00000000..433d12ae --- /dev/null +++ b/src/main/java/com/mojang/serialization/Compressable.java @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +public interface Compressable extends Keyable { + KeyCompressor compressor(final DynamicOps ops); +} diff --git a/src/main/java/com/mojang/serialization/CompressorHolder.java b/src/main/java/com/mojang/serialization/CompressorHolder.java new file mode 100644 index 00000000..362e3f59 --- /dev/null +++ b/src/main/java/com/mojang/serialization/CompressorHolder.java @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; + +import java.util.Map; + +public abstract class CompressorHolder implements Compressable { + private final Map, KeyCompressor> compressors = new Object2ObjectArrayMap<>(); + + @SuppressWarnings("unchecked") + @Override + public KeyCompressor compressor(final DynamicOps ops) { + return (KeyCompressor) compressors.computeIfAbsent(ops, k -> new KeyCompressor<>(ops, keys(ops))); + } +} diff --git a/src/main/java/com/mojang/serialization/DataResult.java b/src/main/java/com/mojang/serialization/DataResult.java new file mode 100644 index 00000000..27513522 --- /dev/null +++ b/src/main/java/com/mojang/serialization/DataResult.java @@ -0,0 +1,347 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.mojang.datafixers.kinds.App; +import com.mojang.datafixers.kinds.Applicative; +import com.mojang.datafixers.kinds.K1; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Function3; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + +/** + * Represents either a successful operation, or a partial operation with an error message and a partial result (if available) + * Also stores an additional lifecycle marker (monoidal) + */ +public class DataResult implements App { + public static final class Mu implements K1 {} + + public static DataResult unbox(final App box) { + return (DataResult) box; + } + + private final Either> result; + private final Lifecycle lifecycle; + + public static DataResult success(final R result) { + return success(result, Lifecycle.experimental()); + } + + public static DataResult error(final String message, final R partialResult) { + return error(message, partialResult, Lifecycle.experimental()); + } + + public static DataResult error(final String message) { + return error(message, Lifecycle.experimental()); + } + + public static DataResult success(final R result, final Lifecycle experimental) { + return new DataResult<>(Either.left(result), experimental); + } + + public static DataResult error(final String message, final R partialResult, final Lifecycle lifecycle) { + return new DataResult<>(Either.right(new PartialResult<>(message, Optional.of(partialResult))), lifecycle); + } + + public static DataResult error(final String message, final Lifecycle lifecycle) { + return new DataResult<>(Either.right(new PartialResult<>(message, Optional.empty())), lifecycle); + } + + public static Function> partialGet(final Function partialGet, final Supplier errorPrefix) { + return name -> Optional.ofNullable(partialGet.apply(name)).map(DataResult::success).orElseGet(() -> error(errorPrefix.get() + name)); + } + + private static DataResult create(final Either> result, final Lifecycle lifecycle) { + return new DataResult<>(result, lifecycle); + } + + private DataResult(final Either> result, final Lifecycle lifecycle) { + this.result = result; + this.lifecycle = lifecycle; + } + + public Either> get() { + return result; + } + + public Optional result() { + return result.left(); + } + + public Lifecycle lifecycle() { + return lifecycle; + } + + public Optional resultOrPartial(final Consumer onError) { + return result.map( + Optional::of, + r -> { + onError.accept(r.message); + return r.partialResult; + } + ); + } + + public R getOrThrow(final boolean allowPartial, final Consumer onError) { + return result.map( + l -> l, + r -> { + onError.accept(r.message); + if (allowPartial && r.partialResult.isPresent()) { + return r.partialResult.get(); + } + throw new RuntimeException(r.message); + } + ); + } + + public Optional> error() { + return result.right(); + } + + public DataResult map(final Function function) { + return create(result.mapBoth( + function, + r -> new PartialResult<>(r.message, r.partialResult.map(function)) + ), lifecycle); + } + + public DataResult promotePartial(final Consumer onError) { + return result.map( + r -> new DataResult<>(Either.left(r), lifecycle), + r -> { + onError.accept(r.message); + return r.partialResult + .map(pr -> new DataResult<>(Either.left(pr), lifecycle)) + .orElseGet(() -> create(Either.right(r), lifecycle)); + } + ); + } + + private static String appendMessages(final String first, final String second) { + return first + "; " + second; + } + + /** + * Applies the function to either full or partial result, in case of partial concatenates errors. + */ + public DataResult flatMap(final Function> function) { + return result.map( + l -> { + final DataResult second = function.apply(l); + return create(second.get(), lifecycle.add(second.lifecycle)); + }, + r -> r.partialResult + .map(value -> { + final DataResult second = function.apply(value); + return create(Either.right(second.get().map( + l2 -> new PartialResult<>(r.message, Optional.of(l2)), + r2 -> new PartialResult<>(appendMessages(r.message, r2.message), r2.partialResult) + )), lifecycle.add(second.lifecycle)); + }) + .orElseGet( + () -> create(Either.right(new PartialResult<>(r.message, Optional.empty())), lifecycle) + ) + ); + } + + public DataResult ap(final DataResult> functionResult) { + return create(result.map( + arg -> functionResult.result.mapBoth( + func -> func.apply(arg), + funcError -> new PartialResult<>(funcError.message, funcError.partialResult.map(f -> f.apply(arg))) + ), + argError -> Either.right(functionResult.result.map( + func -> new PartialResult<>(argError.message, argError.partialResult.map(func)), + funcError -> new PartialResult<>( + appendMessages(argError.message, funcError.message), + argError.partialResult.flatMap(a -> funcError.partialResult.map(f -> f.apply(a))) + ) + )) + ), lifecycle.add(functionResult.lifecycle)); + } + + public DataResult apply2(final BiFunction function, final DataResult second) { + return unbox(instance().apply2(function, this, second)); + } + + public DataResult apply2stable(final BiFunction function, final DataResult second) { + final Applicative instance = instance(); + final DataResult> f = unbox(instance.point(function)).setLifecycle(Lifecycle.stable()); + return unbox(instance.ap2(f, this, second)); + } + + public DataResult apply3(final Function3 function, final DataResult second, final DataResult third) { + return unbox(instance().apply3(function, this, second, third)); + } + + public DataResult setPartial(final Supplier partial) { + return create(result.mapRight(r -> new PartialResult<>(r.message, Optional.of(partial.get()))), lifecycle); + } + + public DataResult setPartial(final R partial) { + return create(result.mapRight(r -> new PartialResult<>(r.message, Optional.of(partial))), lifecycle); + } + + public DataResult mapError(final UnaryOperator function) { + return create(result.mapRight(r -> new PartialResult<>(function.apply(r.message), r.partialResult)), lifecycle); + } + + public DataResult setLifecycle(final Lifecycle lifecycle) { + return create(result, lifecycle); + } + + public DataResult addLifecycle(final Lifecycle lifecycle) { + return create(result, this.lifecycle.add(lifecycle)); + } + + public static Instance instance() { + return Instance.INSTANCE; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final DataResult that = (DataResult) o; + return Objects.equals(result, that.result); + } + + @Override + public int hashCode() { + return Objects.hash(result); + } + + @Override + public String toString() { + return "DataResult[" + result + ']'; + } + + public static class PartialResult { + private final String message; + private final Optional partialResult; + + public PartialResult(final String message, final Optional partialResult) { + this.message = message; + this.partialResult = partialResult; + } + + public PartialResult map(final Function function) { + return new PartialResult<>(message, partialResult.map(function)); + } + + public PartialResult flatMap(final Function> function) { + if (partialResult.isPresent()) { + final PartialResult result = function.apply(partialResult.get()); + return new PartialResult<>(appendMessages(message, result.message), result.partialResult); + } + return new PartialResult<>(message, Optional.empty()); + } + + public String message() { + return message; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final PartialResult that = (PartialResult) o; + return Objects.equals(message, that.message) && Objects.equals(partialResult, that.partialResult); + } + + @Override + public int hashCode() { + return Objects.hash(message, partialResult); + } + + @Override + public String toString() { + return "DynamicException[" + message + ' ' + partialResult + ']'; + } + } + + public enum Instance implements Applicative { + INSTANCE; + + public static final class Mu implements Applicative.Mu {} + + @Override + public App map(final Function func, final App ts) { + return unbox(ts).map(func); + } + + @Override + public App point(final A a) { + return success(a); + } + + @Override + public Function, App> lift1(final App> function) { + return fa -> ap(function, fa); + } + + @Override + public App ap(final App> func, final App arg) { + return unbox(arg).ap(unbox(func)); + } + + @Override + public App ap2(final App> func, final App a, final App b) { + final DataResult> fr = unbox(func); + final DataResult ra = unbox(a); + final DataResult rb = unbox(b); + + // for less recursion + if (fr.result.left().isPresent() + && ra.result.left().isPresent() + && rb.result.left().isPresent() + ) { + return new DataResult<>(Either.left(fr.result.left().get().apply( + ra.result.left().get(), + rb.result.left().get() + )), fr.lifecycle.add(ra.lifecycle).add(rb.lifecycle)); + } + + return Applicative.super.ap2(func, a, b); + } + + @Override + public App ap3(final App> func, final App t1, final App t2, final App t3) { + final DataResult> fr = unbox(func); + final DataResult dr1 = unbox(t1); + final DataResult dr2 = unbox(t2); + final DataResult dr3 = unbox(t3); + + // for less recursion + if (fr.result.left().isPresent() + && dr1.result.left().isPresent() + && dr2.result.left().isPresent() + && dr3.result.left().isPresent() + ) { + return new DataResult<>(Either.left(fr.result.left().get().apply( + dr1.result.left().get(), + dr2.result.left().get(), + dr3.result.left().get() + )), fr.lifecycle.add(dr1.lifecycle).add(dr2.lifecycle).add(dr3.lifecycle)); + } + + return Applicative.super.ap3(func, t1, t2, t3); + } + } +} diff --git a/src/main/java/com/mojang/serialization/Decoder.java b/src/main/java/com/mojang/serialization/Decoder.java new file mode 100644 index 00000000..aa385047 --- /dev/null +++ b/src/main/java/com/mojang/serialization/Decoder.java @@ -0,0 +1,203 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.codecs.FieldDecoder; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +public interface Decoder { + DataResult> decode(final DynamicOps ops, final T input); + + // TODO: rename to read after Type.read is no more + default DataResult parse(final DynamicOps ops, final T input) { + return decode(ops, input).map(Pair::getFirst); + } + + default DataResult> decode(final Dynamic input) { + return decode(input.getOps(), input.getValue()); + } + + default DataResult parse(final Dynamic input) { + return decode(input).map(Pair::getFirst); + } + + default Terminal terminal() { + return this::parse; + } + + default Boxed boxed() { + return this::decode; + } + + default Simple simple() { + return this::parse; + } + + default MapDecoder fieldOf(final String name) { + return new FieldDecoder<>(name, this); + } + + default Decoder flatMap(final Function> function) { + return new Decoder() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return Decoder.this.decode(ops, input).flatMap(p -> function.apply(p.getFirst()).map(r -> Pair.of(r, p.getSecond()))); + } + + @Override + public String toString() { + return Decoder.this.toString() + "[flatMapped]"; + } + }; + } + + default Decoder map(final Function function) { + return new Decoder() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return Decoder.this.decode(ops, input).map(p -> p.mapFirst(function)); + } + + @Override + public String toString() { + return Decoder.this.toString() + "[mapped]"; + } + }; + } + + default Decoder promotePartial(final Consumer onError) { + return new Decoder() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return Decoder.this.decode(ops, input).promotePartial(onError); + } + + @Override + public String toString() { + return Decoder.this.toString() + "[promotePartial]"; + } + }; + } + + default Decoder withLifecycle(final Lifecycle lifecycle) { + return new Decoder() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return Decoder.this.decode(ops, input).setLifecycle(lifecycle); + } + + @Override + public String toString() { + return Decoder.this.toString(); + } + }; + } + + static Decoder ofTerminal(final Terminal terminal) { + return terminal.decoder().map(Function.identity()); + } + + static Decoder ofBoxed(final Boxed boxed) { + return boxed.decoder().map(Function.identity()); + } + + static Decoder ofSimple(final Simple simple) { + return simple.decoder().map(Function.identity()); + } + + static MapDecoder unit(final A instance) { + return unit(() -> instance); + } + + static MapDecoder unit(final Supplier instance) { + return new MapDecoder.Implementation() { + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return DataResult.success(instance.get()); + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.empty(); + } + + @Override + public String toString() { + return "UnitDecoder[" + instance.get() + "]"; + } + }; + } + + static Decoder error(final String error) { + return new Decoder() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return DataResult.error(error); + } + + @Override + public String toString() { + return "ErrorDecoder[" + error + ']'; + } + }; + } + + interface Terminal { + DataResult decode(final DynamicOps ops, final T input); + + default Decoder decoder() { + return new Decoder() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return Terminal.this.decode(ops, input).map(a -> Pair.of(a, ops.empty())); + } + + @Override + public String toString() { + return "TerminalDecoder[" + Terminal.this + "]"; + } + }; + } + } + + interface Boxed { + DataResult> decode(final Dynamic input); + + default Decoder decoder() { + return new Decoder() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return Boxed.this.decode(new Dynamic<>(ops, input)); + } + + @Override + public String toString() { + return "BoxedDecoder[" + Boxed.this + "]"; + } + }; + } + } + + interface Simple { + DataResult decode(final Dynamic input); + + default Decoder decoder() { + return new Decoder() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return Simple.this.decode(new Dynamic<>(ops, input)).map(a -> Pair.of(a, ops.empty())); + } + + @Override + public String toString() { + return "SimpleDecoder[" + Simple.this + "]"; + } + }; + } + } +} diff --git a/src/main/java/com/mojang/serialization/Dynamic.java b/src/main/java/com/mojang/serialization/Dynamic.java new file mode 100644 index 00000000..a75d3db0 --- /dev/null +++ b/src/main/java/com/mojang/serialization/Dynamic.java @@ -0,0 +1,196 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.DataFixUtils; +import com.mojang.datafixers.util.Pair; + +import javax.annotation.Nullable; +import java.nio.ByteBuffer; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +@SuppressWarnings("unused") +public class Dynamic extends DynamicLike { + private final T value; + + public Dynamic(final DynamicOps ops) { + this(ops, ops.empty()); + } + + public Dynamic(final DynamicOps ops, @Nullable final T value) { + super(ops); + this.value = value == null ? ops.empty() : value; + } + + public T getValue() { + return value; + } + + public Dynamic map(final Function function) { + return new Dynamic<>(ops, function.apply(value)); + } + + @SuppressWarnings("unchecked") + public Dynamic castTyped(final DynamicOps ops) { + if (!Objects.equals(this.ops, ops)) { + throw new IllegalStateException("Dynamic type doesn't match"); + } + return (Dynamic) this; + } + + public U cast(final DynamicOps ops) { + return castTyped(ops).getValue(); + } + + public OptionalDynamic merge(final Dynamic value) { + final DataResult merged = ops.mergeToList(this.value, value.cast(ops)); + return new OptionalDynamic<>(ops, merged.map(m -> new Dynamic<>(ops, m))); + } + + public OptionalDynamic merge(final Dynamic key, final Dynamic value) { + final DataResult merged = ops.mergeToMap(this.value, key.cast(ops), value.cast(ops)); + return new OptionalDynamic<>(ops, merged.map(m -> new Dynamic<>(ops, m))); + } + + public DataResult, Dynamic>> getMapValues() { + return ops.getMapValues(value).map(map -> { + final ImmutableMap.Builder, Dynamic> builder = ImmutableMap.builder(); + map.forEach(entry -> builder.put(new Dynamic<>(ops, entry.getFirst()), new Dynamic<>(ops, entry.getSecond()))); + return builder.build(); + }); + } + + public Dynamic updateMapValues(final Function, Dynamic>, Pair, Dynamic>> updater) { + return DataFixUtils.orElse(getMapValues().map(map -> map.entrySet().stream().map(e -> { + final Pair, Dynamic> pair = updater.apply(Pair.of(e.getKey(), e.getValue())); + return Pair.of(pair.getFirst().castTyped(ops), pair.getSecond().castTyped(ops)); + }).collect(Pair.toMap())).map(this::createMap).result(), this); + } + + @Override + public DataResult asNumber() { + return ops.getNumberValue(value); + } + + @Override + public DataResult asString() { + return ops.getStringValue(value); + } + + @Override + public DataResult>> asStreamOpt() { + return ops.getStream(value).map(s -> s.map(e -> new Dynamic<>(ops, e))); + } + + @Override + public DataResult, Dynamic>>> asMapOpt() { + return ops.getMapValues(value).map(s -> s.map(p -> Pair.of(new Dynamic<>(ops, p.getFirst()), new Dynamic<>(ops, p.getSecond())))); + } + + @Override + public DataResult asByteBufferOpt() { + return ops.getByteBuffer(value); + } + + @Override + public DataResult asIntStreamOpt() { + return ops.getIntStream(value); + } + + @Override + public DataResult asLongStreamOpt() { + return ops.getLongStream(value); + } + + @Override + public OptionalDynamic get(final String key) { + return new OptionalDynamic<>(ops, ops.getMap(value).flatMap(m -> { + final T value = m.get(key); + if (value == null) { + return DataResult.error("key missing: " + key + " in " + this.value); + } + return DataResult.success(new Dynamic<>(ops, value)); + })); + } + + @Override + public DataResult getGeneric(final T key) { + return ops.getGeneric(value, key); + } + + public Dynamic remove(final String key) { + return map(v -> ops.remove(v, key)); + } + + public Dynamic set(final String key, final Dynamic value) { + return map(v -> ops.set(v, key, value.cast(ops))); + } + + public Dynamic update(final String key, final Function, Dynamic> function) { + return map(v -> ops.update(v, key, value -> function.apply(new Dynamic<>(ops, value)).cast(ops))); + } + + public Dynamic updateGeneric(final T key, final Function function) { + return map(v -> ops.updateGeneric(v, key, function)); + } + + @Override + public DataResult getElement(final String key) { + return getElementGeneric(ops.createString(key)); + } + + @Override + public DataResult getElementGeneric(final T key) { + return ops.getGeneric(value, key); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final Dynamic dynamic = (Dynamic) o; + return Objects.equals(ops, dynamic.ops) && Objects.equals(value, dynamic.value); + } + + @Override + public int hashCode() { + return Objects.hash(ops, value); + } + + @Override + public String toString() { + return String.format("%s[%s]", ops, value); + } + + public Dynamic convert(final DynamicOps outOps) { + return new Dynamic<>(outOps, convert(ops, outOps, value)); + } + + public V into(final Function, ? extends V> action) { + return action.apply(this); + } + + @Override + public DataResult> decode(final Decoder decoder) { + return decoder.decode(ops, value).map(p -> p.mapFirst(Function.identity())); + } + + @SuppressWarnings("unchecked") + public static T convert(final DynamicOps inOps, final DynamicOps outOps, final S input) { + if (Objects.equals(inOps, outOps)) { + return (T) input; + } + + return inOps.convertTo(outOps, input); + } +} diff --git a/src/main/java/com/mojang/serialization/DynamicLike.java b/src/main/java/com/mojang/serialization/DynamicLike.java new file mode 100644 index 00000000..1ef30b43 --- /dev/null +++ b/src/main/java/com/mojang/serialization/DynamicLike.java @@ -0,0 +1,234 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.kinds.App; +import com.mojang.datafixers.kinds.ListBox; +import com.mojang.datafixers.util.Function3; +import com.mojang.datafixers.util.Pair; +import org.apache.commons.lang3.mutable.MutableObject; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +@SuppressWarnings("unused") +public abstract class DynamicLike { + protected final DynamicOps ops; + + public DynamicLike(final DynamicOps ops) { + this.ops = ops; + } + + public DynamicOps getOps() { + return ops; + } + + public abstract DataResult asNumber(); + public abstract DataResult asString(); + public abstract DataResult>> asStreamOpt(); + public abstract DataResult, Dynamic>>> asMapOpt(); + public abstract DataResult asByteBufferOpt(); + public abstract DataResult asIntStreamOpt(); + public abstract DataResult asLongStreamOpt(); + public abstract OptionalDynamic get(String key); + public abstract DataResult getGeneric(T key); + public abstract DataResult getElement(String key); + public abstract DataResult getElementGeneric(T key); + + public abstract DataResult> decode(final Decoder decoder); + + public DataResult> asListOpt(final Function, U> deserializer) { + return asStreamOpt().map(stream -> stream.map(deserializer).collect(Collectors.toList())); + } + + public DataResult> asMapOpt(final Function, K> keyDeserializer, final Function, V> valueDeserializer) { + return asMapOpt().map(map -> { + final ImmutableMap.Builder builder = ImmutableMap.builder(); + map.forEach(entry -> + builder.put(keyDeserializer.apply(entry.getFirst()), valueDeserializer.apply(entry.getSecond())) + ); + return builder.build(); + }); + } + + public DataResult read(final Decoder decoder) { + return decode(decoder).map(Pair::getFirst); + } + + public DataResult> readList(final Decoder decoder) { + return asStreamOpt() + .map(s -> s.map(d -> d.read(decoder)).collect(Collectors.>toList())) + .flatMap(l -> DataResult.unbox(ListBox.flip(DataResult.instance(), l))); + } + + public DataResult> readList(final Function, ? extends DataResult> decoder) { + return asStreamOpt() + .map(s -> s.map(decoder).map(r -> r.map(e -> (E) e)).collect(Collectors.>toList())) + .flatMap(l -> DataResult.unbox(ListBox.flip(DataResult.instance(), l))); + } + + public DataResult>> readMap(final Decoder keyDecoder, final Decoder valueDecoder) { + return asMapOpt() + .map(stream -> stream.map(p -> p.getFirst().read(keyDecoder).flatMap(f -> p.getSecond().read(valueDecoder).map(s -> Pair.of(f, s)))).collect(Collectors.>>toList())) + .flatMap(l -> DataResult.unbox(ListBox.flip(DataResult.instance(), l))); + } + + public DataResult>> readMap(final Decoder keyDecoder, final Function> valueDecoder) { + return asMapOpt() + .map(stream -> stream.map(p -> p.getFirst().read(keyDecoder).flatMap(f -> p.getSecond().read(valueDecoder.apply(f)).map(s -> Pair.of(f, s)))).collect(Collectors.>>toList())) + .flatMap(l -> DataResult.unbox(ListBox.flip(DataResult.instance(), l))); + } + + public DataResult readMap(final DataResult empty, final Function3, Dynamic, DataResult> combiner) { + return asMapOpt().flatMap(stream -> { + // TODO: AtomicReference.getPlain/setPlain in java9+ + final MutableObject> result = new MutableObject<>(empty); + stream.forEach(p -> result.setValue(result.getValue().flatMap(r -> combiner.apply(r, p.getFirst(), p.getSecond())))); + return result.getValue(); + }); + } + + public Number asNumber(final Number defaultValue) { + return asNumber().result().orElse(defaultValue); + } + + public int asInt(final int defaultValue) { + return asNumber(defaultValue).intValue(); + } + + public long asLong(final long defaultValue) { + return asNumber(defaultValue).longValue(); + } + + public float asFloat(final float defaultValue) { + return asNumber(defaultValue).floatValue(); + } + + public double asDouble(final double defaultValue) { + return asNumber(defaultValue).doubleValue(); + } + + public byte asByte(final byte defaultValue) { + return asNumber(defaultValue).byteValue(); + } + + public short asShort(final short defaultValue) { + return asNumber(defaultValue).shortValue(); + } + + public boolean asBoolean(final boolean defaultValue) { + return asNumber(defaultValue ? 1 : 0).intValue() != 0; + } + + public String asString(final String defaultValue) { + return asString().result().orElse(defaultValue); + } + + public Stream> asStream() { + return asStreamOpt().result().orElseGet(Stream::empty); + } + + public ByteBuffer asByteBuffer() { + return asByteBufferOpt().result().orElseGet(() -> ByteBuffer.wrap(new byte[0])); + } + + public IntStream asIntStream() { + return asIntStreamOpt().result().orElseGet(IntStream::empty); + } + + public LongStream asLongStream() { + return asLongStreamOpt().result().orElseGet(LongStream::empty); + } + + public List asList(final Function, U> deserializer) { + return asListOpt(deserializer).result().orElseGet(ImmutableList::of); + } + + public Map asMap(final Function, K> keyDeserializer, final Function, V> valueDeserializer) { + return asMapOpt(keyDeserializer, valueDeserializer).result().orElseGet(ImmutableMap::of); + } + + public T getElement(final String key, final T defaultValue) { + return getElement(key).result().orElse(defaultValue); + } + + public T getElementGeneric(final T key, final T defaultValue) { + return getElementGeneric(key).result().orElse(defaultValue); + } + + public Dynamic emptyList() { + return new Dynamic<>(ops, ops.emptyList()); + } + + public Dynamic emptyMap() { + return new Dynamic<>(ops, ops.emptyMap()); + } + + public Dynamic createNumeric(final Number i) { + return new Dynamic<>(ops, ops.createNumeric(i)); + } + + public Dynamic createByte(final byte value) { + return new Dynamic<>(ops, ops.createByte(value)); + } + + public Dynamic createShort(final short value) { + return new Dynamic<>(ops, ops.createShort(value)); + } + + public Dynamic createInt(final int value) { + return new Dynamic<>(ops, ops.createInt(value)); + } + + public Dynamic createLong(final long value) { + return new Dynamic<>(ops, ops.createLong(value)); + } + + public Dynamic createFloat(final float value) { + return new Dynamic<>(ops, ops.createFloat(value)); + } + + public Dynamic createDouble(final double value) { + return new Dynamic<>(ops, ops.createDouble(value)); + } + + public Dynamic createBoolean(final boolean value) { + return new Dynamic<>(ops, ops.createBoolean(value)); + } + + public Dynamic createString(final String value) { + return new Dynamic<>(ops, ops.createString(value)); + } + + public Dynamic createList(final Stream> input) { + return new Dynamic<>(ops, ops.createList(input.map(element -> element.cast(ops)))); + } + + public Dynamic createMap(final Map, ? extends Dynamic> map) { + final ImmutableMap.Builder builder = ImmutableMap.builder(); + for (final Map.Entry, ? extends Dynamic> entry : map.entrySet()) { + builder.put(entry.getKey().cast(ops), entry.getValue().cast(ops)); + } + return new Dynamic<>(ops, ops.createMap(builder.build())); + } + + public Dynamic createByteList(final ByteBuffer input) { + return new Dynamic<>(ops, ops.createByteList(input)); + } + + public Dynamic createIntList(final IntStream input) { + return new Dynamic<>(ops, ops.createIntList(input)); + } + + public Dynamic createLongList(final LongStream input) { + return new Dynamic<>(ops, ops.createLongList(input)); + } +} diff --git a/src/main/java/com/mojang/serialization/DynamicOps.java b/src/main/java/com/mojang/serialization/DynamicOps.java new file mode 100644 index 00000000..a0ffdc0f --- /dev/null +++ b/src/main/java/com/mojang/serialization/DynamicOps.java @@ -0,0 +1,286 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.util.Function3; +import com.mojang.datafixers.util.Pair; +import org.apache.commons.lang3.mutable.MutableObject; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +public interface DynamicOps { + T empty(); + + default T emptyMap() { + return createMap(ImmutableMap.of()); + } + + default T emptyList() { + return createList(Stream.empty()); + } + + U convertTo(DynamicOps outOps, T input); + + DataResult getNumberValue(T input); + + default Number getNumberValue(final T input, final Number defaultValue) { + return getNumberValue(input).result().orElse(defaultValue); + } + + T createNumeric(Number i); + + default T createByte(final byte value) { + return createNumeric(value); + } + + default T createShort(final short value) { + return createNumeric(value); + } + + default T createInt(final int value) { + return createNumeric(value); + } + + default T createLong(final long value) { + return createNumeric(value); + } + + default T createFloat(final float value) { + return createNumeric(value); + } + + default T createDouble(final double value) { + return createNumeric(value); + } + + default DataResult getBooleanValue(final T input) { + return getNumberValue(input).map(number -> number.byteValue() != 0); + } + + default T createBoolean(final boolean value) { + return createByte((byte) (value ? 1 : 0)); + } + + DataResult getStringValue(T input); + + T createString(String value); + + /** + * Only successful if first argument is a list/array or empty + */ + DataResult mergeToList(T list, T value); + + default DataResult mergeToList(final T list, final List values) { + DataResult result = DataResult.success(list); + + for (final T value : values) { + result = result.flatMap(r -> mergeToList(r, value)); + } + return result; + } + + /** + * Only successful if first argument is a map or empty + */ + DataResult mergeToMap(T map, T key, T value); + + default DataResult mergeToMap(final T map, final Map values) { + return mergeToMap(map, MapLike.forMap(values, this)); + } + + default DataResult mergeToMap(final T map, final MapLike values) { + // TODO: AtomicReference.getPlain/setPlain in java9+ + final MutableObject> result = new MutableObject<>(DataResult.success(map)); + + values.entries().forEach(entry -> + result.setValue(result.getValue().flatMap(r -> mergeToMap(r, entry.getFirst(), entry.getSecond()))) + ); + return result.getValue(); + } + + /** + * Only successful if first argument is empty + */ + default DataResult mergeToPrimitive(final T prefix, final T value) { + if (!Objects.equals(prefix, empty())) { + return DataResult.error("Do not know how to append a primitive value " + value + " to " + prefix, value); + } + return DataResult.success(value); + } + + DataResult>> getMapValues(T input); + + default DataResult>> getMapEntries(final T input) { + return getMapValues(input).map(s -> c -> s.forEach(p -> c.accept(p.getFirst(), p.getSecond()))); + } + + T createMap(Stream> map); + + default DataResult> getMap(final T input) { + return getMapValues(input).flatMap(s -> { + try { + return DataResult.success(MapLike.forMap(s.collect(Pair.toMap()), this)); + } catch (final IllegalStateException e) { + return DataResult.error("Error while building map: " + e.getMessage()); + } + }); + } + + default T createMap(final Map map) { + return createMap(map.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue()))); + } + + DataResult> getStream(T input); + + default DataResult>> getList(final T input) { + return getStream(input).map(s -> s::forEach); + } + + T createList(Stream input); + + default DataResult getByteBuffer(final T input) { + return getStream(input).flatMap(stream -> { + final List list = stream.collect(Collectors.toList()); + if (list.stream().allMatch(element -> getNumberValue(element).result().isPresent())) { + final ByteBuffer buffer = ByteBuffer.wrap(new byte[list.size()]); + for (int i = 0; i < list.size(); i++) { + buffer.put(i, getNumberValue(list.get(i)).result().get().byteValue()); + } + return DataResult.success(buffer); + } + return DataResult.error("Some elements are not bytes: " + input); + }); + } + + default T createByteList(final ByteBuffer input) { + return createList(IntStream.range(0, input.capacity()).mapToObj(i -> createByte(input.get(i)))); + } + + default DataResult getIntStream(final T input) { + return getStream(input).flatMap(stream -> { + final List list = stream.collect(Collectors.toList()); + if (list.stream().allMatch(element -> getNumberValue(element).result().isPresent())) { + return DataResult.success(list.stream().mapToInt(element -> getNumberValue(element).result().get().intValue())); + } + return DataResult.error("Some elements are not ints: " + input); + }); + } + + default T createIntList(final IntStream input) { + return createList(input.mapToObj(this::createInt)); + } + + default DataResult getLongStream(final T input) { + return getStream(input).flatMap(stream -> { + final List list = stream.collect(Collectors.toList()); + if (list.stream().allMatch(element -> getNumberValue(element).result().isPresent())) { + return DataResult.success(list.stream().mapToLong(element -> getNumberValue(element).result().get().longValue())); + } + return DataResult.error("Some elements are not longs: " + input); + }); + } + + default T createLongList(final LongStream input) { + return createList(input.mapToObj(this::createLong)); + } + + T remove(T input, String key); + + default boolean compressMaps() { + return false; + } + + default DataResult get(final T input, final String key) { + return getGeneric(input, createString(key)); + } + + default DataResult getGeneric(final T input, final T key) { + return getMap(input).flatMap(map -> Optional.ofNullable(map.get(key)) + .map(DataResult::success) + .orElseGet(() -> DataResult.error("No element " + key + " in the map " + input)) + ); + } + + // TODO: eats error if input is not a map + default T set(final T input, final String key, final T value) { + return mergeToMap(input, createString(key), value).result().orElse(input); + } + + // TODO: eats error if input is not a map + default T update(final T input, final String key, final Function function) { + return get(input, key).map(value -> set(input, key, function.apply(value))).result().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))).result().orElse(input); + } + + default ListBuilder listBuilder() { + return new ListBuilder.Builder<>(this); + } + + default DataResult list(final Iterable list, final T prefix, final Encoder encoder) { + final ListBuilder builder = listBuilder(); + builder.addAll(list, encoder); + return builder.build(prefix); + } + + default DataResult list(final Iterable list, final T prefix, final Function> elementSerializer) { + final ListBuilder builder = listBuilder(); + list.forEach(element -> builder.add(elementSerializer.apply(element))); + return builder.build(prefix); + } + + default RecordBuilder mapBuilder() { + return new RecordBuilder.MapBuilder<>(this); + } + + default DataResult map(final Map map, final T prefix, final Function> keySerializer, final Function> elementSerializer) { + final RecordBuilder builder = mapBuilder(); + map.forEach((key, value) -> builder.add(keySerializer.apply(key), elementSerializer.apply(value))); + return builder.build(prefix); + } + + default DataResult readMap(final T input, final DataResult empty, final Function3> combiner) { + return getMapValues(input).flatMap(stream -> { + // TODO: AtomicReference.getPlain/setPlain in java9+ + final MutableObject> result = new MutableObject<>(empty); + stream.forEach(p -> result.setValue(result.getValue().flatMap(r -> combiner.apply(r, p.getFirst(), p.getSecond())))); + return result.getValue(); + }); + } + + default Function> withEncoder(final Encoder encoder) { + return e -> encoder.encodeStart(this, e); + } + + default Function>> withDecoder(final Decoder decoder) { + return t -> decoder.decode(this, t); + } + + default Function> withParser(final Decoder decoder) { + return t -> decoder.parse(this, t); + } + + default U convertList(final DynamicOps outOps, final T input) { + return outOps.createList(getStream(input).result().orElse(Stream.empty()).map(e -> convertTo(outOps, e))); + } + + default U convertMap(final DynamicOps outOps, final T input) { + return outOps.createMap(getMapValues(input).result().orElse(Stream.empty()).map(e -> + Pair.of(convertTo(outOps, e.getFirst()), convertTo(outOps, e.getSecond())) + )); + } +} diff --git a/src/main/java/com/mojang/serialization/Encoder.java b/src/main/java/com/mojang/serialization/Encoder.java new file mode 100644 index 00000000..fc76059b --- /dev/null +++ b/src/main/java/com/mojang/serialization/Encoder.java @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.mojang.serialization.codecs.FieldEncoder; + +import java.util.function.Function; +import java.util.stream.Stream; + +public interface Encoder { + DataResult encode(final A input, final DynamicOps ops, final T prefix); + + default DataResult encodeStart(final DynamicOps ops, final A input) { + return encode(input, ops, ops.empty()); + } + + default MapEncoder fieldOf(final String name) { + return new FieldEncoder<>(name, this); + } + + default Encoder comap(final Function function) { + return new Encoder() { + @Override + public DataResult encode(final B input, final DynamicOps ops, final T prefix) { + return Encoder.this.encode(function.apply(input), ops, prefix); + } + + @Override + public String toString() { + return Encoder.this.toString() + "[comapped]"; + } + }; + } + + default Encoder flatComap(final Function> function) { + return new Encoder() { + @Override + public DataResult encode(final B input, final DynamicOps ops, final T prefix) { + return function.apply(input).flatMap(a -> Encoder.this.encode(a, ops, prefix)); + } + + @Override + public String toString() { + return Encoder.this.toString() + "[flatComapped]"; + } + }; + } + + default Encoder withLifecycle(final Lifecycle lifecycle) { + return new Encoder() { + @Override + public DataResult encode(final A input, final DynamicOps ops, final T prefix) { + return Encoder.this.encode(input, ops, prefix).setLifecycle(lifecycle); + } + + @Override + public String toString() { + return Encoder.this.toString(); + } + }; + } + + static MapEncoder empty() { + return new MapEncoder.Implementation() { + @Override + public RecordBuilder encode(final A input, final DynamicOps ops, final RecordBuilder prefix) { + return prefix; + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.empty(); + } + + @Override + public String toString() { + return "EmptyEncoder"; + } + }; + } + + static Encoder error(final String error) { + return new Encoder() { + @Override + public DataResult encode(final A input, final DynamicOps ops, final T prefix) { + return DataResult.error(error + " " + input); + } + + @Override + public String toString() { + return "EmptyEncoder[" + error + "]"; + } + }; + } +} diff --git a/src/main/java/com/mojang/serialization/JsonOps.java b/src/main/java/com/mojang/serialization/JsonOps.java new file mode 100644 index 00000000..d4f42a41 --- /dev/null +++ b/src/main/java/com/mojang/serialization/JsonOps.java @@ -0,0 +1,428 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.mojang.datafixers.util.Pair; + +import javax.annotation.Nullable; +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class JsonOps implements DynamicOps { + public static final JsonOps INSTANCE = new JsonOps(false); + public static final JsonOps COMPRESSED = new JsonOps(true); + + private final boolean compressed; + + protected JsonOps(final boolean compressed) { + this.compressed = compressed; + } + + @Override + public JsonElement empty() { + return JsonNull.INSTANCE; + } + + @Override + public U convertTo(final DynamicOps outOps, final 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 DataResult getNumberValue(final JsonElement input) { + if (input instanceof JsonPrimitive) { + if (input.getAsJsonPrimitive().isNumber()) { + return DataResult.success(input.getAsNumber()); + } else if (input.getAsJsonPrimitive().isBoolean()) { + return DataResult.success(input.getAsBoolean() ? 1 : 0); + } + if (compressed && input.getAsJsonPrimitive().isString()) { + try { + return DataResult.success(Integer.parseInt(input.getAsString())); + } catch (final NumberFormatException e) { + return DataResult.error("Not a number: " + e + " " + input); + } + } + } + if (input instanceof JsonPrimitive && input.getAsJsonPrimitive().isBoolean()) { + return DataResult.success(input.getAsJsonPrimitive().getAsBoolean() ? 1 : 0); + } + return DataResult.error("Not a number: " + input); + } + + @Override + public JsonElement createNumeric(final Number i) { + return new JsonPrimitive(i); + } + + @Override + public DataResult getBooleanValue(final JsonElement input) { + if (input instanceof JsonPrimitive) { + if (input.getAsJsonPrimitive().isBoolean()) { + return DataResult.success(input.getAsBoolean()); + } else if (input.getAsJsonPrimitive().isNumber()) { + return DataResult.success(input.getAsNumber().byteValue() != 0); + } + } + return DataResult.error("Not a boolean: " + input); + } + + @Override + public JsonElement createBoolean(final boolean value) { + return new JsonPrimitive(value); + } + + @Override + public DataResult getStringValue(final JsonElement input) { + if (input instanceof JsonPrimitive) { + if (input.getAsJsonPrimitive().isString() || input.getAsJsonPrimitive().isNumber() && compressed) { + return DataResult.success(input.getAsString()); + } + } + return DataResult.error("Not a string: " + input); + } + + @Override + public JsonElement createString(final String value) { + return new JsonPrimitive(value); + } + + @Override + public DataResult mergeToList(final JsonElement list, final JsonElement value) { + if (!(list instanceof JsonArray) && list != empty()) { + return DataResult.error("mergeToList called with not a list: " + list, list); + } + + final JsonArray result = new JsonArray(); + if (list != empty()) { + result.addAll(list.getAsJsonArray()); + } + result.add(value); + return DataResult.success(result); + } + + @Override + public DataResult mergeToList(final JsonElement list, final List values) { + if (!(list instanceof JsonArray) && list != empty()) { + return DataResult.error("mergeToList called with not a list: " + list, list); + } + + final JsonArray result = new JsonArray(); + if (list != empty()) { + result.addAll(list.getAsJsonArray()); + } + values.forEach(result::add); + return DataResult.success(result); + } + + @Override + public DataResult mergeToMap(final JsonElement map, final JsonElement key, final JsonElement value) { + if (!(map instanceof JsonObject) && map != empty()) { + return DataResult.error("mergeToMap called with not a map: " + map, map); + } + if (!(key instanceof JsonPrimitive) || !key.getAsJsonPrimitive().isString() && !compressed) { + return DataResult.error("key is not a string: " + key, map); + } + + 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 DataResult.success(output); + } + + @Override + public DataResult mergeToMap(final JsonElement map, final MapLike values) { + if (!(map instanceof JsonObject) && map != empty()) { + return DataResult.error("mergeToMap called with not a map: " + map, map); + } + + final JsonObject output = new JsonObject(); + if (map != empty()) { + map.getAsJsonObject().entrySet().forEach(entry -> output.add(entry.getKey(), entry.getValue())); + } + + final List missed = Lists.newArrayList(); + + values.entries().forEach(entry -> { + final JsonElement key = entry.getFirst(); + if (!(key instanceof JsonPrimitive) || !key.getAsJsonPrimitive().isString() && !compressed) { + missed.add(key); + return; + } + output.add(key.getAsString(), entry.getSecond()); + }); + + if (!missed.isEmpty()) { + return DataResult.error("some keys are not strings: " + missed, output); + } + + return DataResult.success(output); + } + + @Override + public DataResult>> getMapValues(final JsonElement input) { + if (!(input instanceof JsonObject)) { + return DataResult.error("Not a JSON object: " + input); + } + return DataResult.success(input.getAsJsonObject().entrySet().stream().map(entry -> Pair.of(new JsonPrimitive(entry.getKey()), entry.getValue() instanceof JsonNull ? null : entry.getValue()))); + } + + @Override + public DataResult>> getMapEntries(final JsonElement input) { + if (!(input instanceof JsonObject)) { + return DataResult.error("Not a JSON object: " + input); + } + return DataResult.success(c -> { + for (final Map.Entry entry : input.getAsJsonObject().entrySet()) { + c.accept(createString(entry.getKey()), entry.getValue() instanceof JsonNull ? null : entry.getValue()); + } + }); + } + + @Override + public DataResult> getMap(final JsonElement input) { + if (!(input instanceof JsonObject)) { + return DataResult.error("Not a JSON object: " + input); + } + final JsonObject object = input.getAsJsonObject(); + return DataResult.success(new MapLike() { + @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 "MapLike[" + object + "]"; + } + }); + } + + @Override + public JsonElement createMap(final Stream> map) { + final JsonObject result = new JsonObject(); + map.forEach(p -> result.add(p.getFirst().getAsString(), p.getSecond())); + return result; + } + + @Override + public DataResult> getStream(final JsonElement input) { + if (input instanceof JsonArray) { + return DataResult.success(StreamSupport.stream(input.getAsJsonArray().spliterator(), false).map(e -> e instanceof JsonNull ? null : e)); + } + return DataResult.error("Not a json array: " + input); + } + + @Override + public DataResult>> getList(final JsonElement input) { + if (input instanceof JsonArray) { + return DataResult.success(c -> { + for (final JsonElement element : input.getAsJsonArray()) { + c.accept(element instanceof JsonNull ? null : element); + } + }); + } + return DataResult.error("Not a json array: " + input); + } + + @Override + public JsonElement createList(final Stream input) { + final JsonArray result = new JsonArray(); + input.forEach(result::add); + return result; + } + + @Override + public JsonElement remove(final JsonElement input, final 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; + } + + @Override + public String toString() { + return "JSON"; + } + + @Override + public ListBuilder listBuilder() { + return new ArrayBuilder(); + } + + private static final class ArrayBuilder implements ListBuilder { + private DataResult builder = DataResult.success(new JsonArray(), Lifecycle.stable()); + + @Override + public DynamicOps ops() { + return INSTANCE; + } + + @Override + public ListBuilder add(final JsonElement value) { + builder = builder.map(b -> { + b.add(value); + return b; + }); + return this; + } + + @Override + public ListBuilder add(final DataResult value) { + builder = builder.apply2stable((b, element) -> { + b.add(element); + return b; + }, value); + return this; + } + + @Override + public ListBuilder withErrorsFrom(final DataResult result) { + builder = builder.flatMap(r -> result.map(v -> r)); + return this; + } + + @Override + public ListBuilder mapError(final UnaryOperator onError) { + builder = builder.mapError(onError); + return this; + } + + @Override + public DataResult build(final JsonElement prefix) { + final DataResult result = builder.flatMap(b -> { + if (!(prefix instanceof JsonArray) && prefix != ops().empty()) { + return DataResult.error("Cannot append a list to not a list: " + prefix, prefix); + } + + final JsonArray array = new JsonArray(); + if (prefix != ops().empty()) { + array.addAll(prefix.getAsJsonArray()); + } + array.addAll(b); + return DataResult.success(array, Lifecycle.stable()); + }); + + builder = DataResult.success(new JsonArray(), Lifecycle.stable()); + return result; + } + } + + @Override + public boolean compressMaps() { + return compressed; + } + + @Override + public RecordBuilder mapBuilder() { + return new JsonRecordBuilder(); + } + + private class JsonRecordBuilder extends RecordBuilder.AbstractStringBuilder { + protected JsonRecordBuilder() { + super(JsonOps.this); + } + + @Override + protected JsonObject initBuilder() { + return new JsonObject(); + } + + @Override + protected JsonObject append(final String key, final JsonElement value, final JsonObject builder) { + builder.add(key, value); + return builder; + } + + @Override + protected DataResult build(final JsonObject builder, final JsonElement prefix) { + if (prefix == null || prefix instanceof JsonNull) { + return DataResult.success(builder); + } + if (prefix instanceof JsonObject) { + final JsonObject result = new JsonObject(); + for (final Map.Entry entry : prefix.getAsJsonObject().entrySet()) { + result.add(entry.getKey(), entry.getValue()); + } + for (final Map.Entry entry : builder.entrySet()) { + result.add(entry.getKey(), entry.getValue()); + } + return DataResult.success(result); + } + return DataResult.error("mergeToMap called with not a map: " + prefix, prefix); + } + } +} diff --git a/src/main/java/com/mojang/serialization/KeyCompressor.java b/src/main/java/com/mojang/serialization/KeyCompressor.java new file mode 100644 index 00000000..76422852 --- /dev/null +++ b/src/main/java/com/mojang/serialization/KeyCompressor.java @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2IntArrayMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; + +import java.util.stream.Stream; + +public final class KeyCompressor { + private final Int2ObjectMap decompress = new Int2ObjectArrayMap<>(); + private final Object2IntMap compress = new Object2IntArrayMap<>(); + private final Object2IntMap compressString = new Object2IntArrayMap<>(); + private final int size; + private final DynamicOps ops; + + public KeyCompressor(final DynamicOps ops, final Stream keyStream) { + this.ops = ops; + + compressString.defaultReturnValue(-1); + + keyStream.forEach(key -> { + if (compress.containsKey(key)) { + return; + } + final int next = compress.size(); + compress.put(key, next); + ops.getStringValue(key).result().ifPresent(k -> + compressString.put(k, next) + ); + decompress.put(next, key); + }); + + size = compress.size(); + } + + public T decompress(final int key) { + return decompress.get(key); + } + + public int compress(final String key) { + final int id = compressString.getInt(key); + return id == -1 ? compress(ops.createString(key)) : id; + } + + public int compress(final T key) { + return compress.get(key); + } + + public int size() { + return size; + } +} diff --git a/src/main/java/com/mojang/serialization/Keyable.java b/src/main/java/com/mojang/serialization/Keyable.java new file mode 100644 index 00000000..676c04bc --- /dev/null +++ b/src/main/java/com/mojang/serialization/Keyable.java @@ -0,0 +1,17 @@ +package com.mojang.serialization; + +import java.util.function.Supplier; +import java.util.stream.Stream; + +public interface Keyable { + Stream keys(DynamicOps ops); + + static Keyable forStrings(final Supplier> keys) { + return new Keyable() { + @Override + public Stream keys(final DynamicOps ops) { + return keys.get().map(ops::createString); + } + }; + } +} diff --git a/src/main/java/com/mojang/serialization/Lifecycle.java b/src/main/java/com/mojang/serialization/Lifecycle.java new file mode 100644 index 00000000..7a044c38 --- /dev/null +++ b/src/main/java/com/mojang/serialization/Lifecycle.java @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +public class Lifecycle { + private static final Lifecycle STABLE = new Lifecycle() { + @Override + public String toString() { + return "Stable"; + } + }; + private static final Lifecycle EXPERIMENTAL = new Lifecycle() { + @Override + public String toString() { + return "Experimental"; + } + }; + + private Lifecycle() { + } + + public static final class Deprecated extends Lifecycle { + private final int since; + + public Deprecated(final int since) { + this.since = since; + } + + public int since() { + return since; + } + } + + public static Lifecycle experimental() { + return EXPERIMENTAL; + } + + public static Lifecycle stable() { + return STABLE; + } + + public static Lifecycle deprecated(final int since) { + return new Deprecated(since); + } + + public Lifecycle add(final Lifecycle other) { + if (this == EXPERIMENTAL || other == EXPERIMENTAL) { + return EXPERIMENTAL; + } + if (this instanceof Deprecated) { + if (other instanceof Deprecated && ((Deprecated) other).since < ((Deprecated) this).since) { + return other; + } + return this; + } + if (other instanceof Deprecated) { + return other; + } + return STABLE; + } +} diff --git a/src/main/java/com/mojang/serialization/ListBuilder.java b/src/main/java/com/mojang/serialization/ListBuilder.java new file mode 100644 index 00000000..c68ae76a --- /dev/null +++ b/src/main/java/com/mojang/serialization/ListBuilder.java @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.google.common.collect.ImmutableList; + +import java.util.function.UnaryOperator; + +public interface ListBuilder { + DynamicOps ops(); + + DataResult build(T prefix); + + ListBuilder add(final T value); + + ListBuilder add(final DataResult value); + + ListBuilder withErrorsFrom(final DataResult result); + + ListBuilder mapError(UnaryOperator onError); + + default DataResult build(final DataResult prefix) { + return prefix.flatMap(this::build); + } + + default ListBuilder add(final E value, final Encoder encoder) { + return add(encoder.encodeStart(ops(), value)); + } + + default ListBuilder addAll(final Iterable values, final Encoder encoder) { + values.forEach(v -> encoder.encode(v, ops(), ops().empty())); + return this; + } + + final class Builder implements ListBuilder { + private final DynamicOps ops; + private DataResult> builder = DataResult.success(ImmutableList.builder(), Lifecycle.stable()); + + public Builder(final DynamicOps ops) { + this.ops = ops; + } + + @Override + public DynamicOps ops() { + return ops; + } + + @Override + public ListBuilder add(final T value) { + builder = builder.map(b -> b.add(value)); + return this; + } + + @Override + public ListBuilder add(final DataResult value) { + builder = builder.apply2stable(ImmutableList.Builder::add, value); + return this; + } + + @Override + public ListBuilder withErrorsFrom(final DataResult result) { + builder = builder.flatMap(r -> result.map(v -> r)); + return this; + } + + @Override + public ListBuilder mapError(final UnaryOperator onError) { + builder = builder.mapError(onError); + return this; + } + + @Override + public DataResult build(final T prefix) { + final DataResult result = builder.flatMap(b -> ops.mergeToList(prefix, b.build())); + builder = DataResult.success(ImmutableList.builder(), Lifecycle.stable()); + return result; + } + } +} diff --git a/src/main/java/com/mojang/serialization/MapCodec.java b/src/main/java/com/mojang/serialization/MapCodec.java new file mode 100644 index 00000000..738149f7 --- /dev/null +++ b/src/main/java/com/mojang/serialization/MapCodec.java @@ -0,0 +1,287 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.mojang.datafixers.DataFixUtils; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +public abstract class MapCodec extends CompressorHolder implements MapDecoder, MapEncoder { + public final RecordCodecBuilder forGetter(final Function getter) { + return RecordCodecBuilder.of(getter, this); + } + + public static MapCodec of(final MapEncoder encoder, final MapDecoder decoder) { + return of(encoder, decoder, "MapCodec[" + encoder + " " + decoder + "]"); + } + + public static MapCodec of(final MapEncoder encoder, final MapDecoder decoder, final String name) { + return new MapCodec() { + @Override + public Stream keys(final DynamicOps ops) { + return Stream.concat(encoder.keys(ops), decoder.keys(ops)); + } + + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return decoder.decode(ops, input); + } + + @Override + public RecordBuilder encode(final A input, final DynamicOps ops, final RecordBuilder prefix) { + return encoder.encode(input, ops, prefix); + } + + @Override + public String toString() { + return name; + } + }; + } + + public MapCodec fieldOf(final String name) { + return codec().fieldOf(name); + } + + @Override + public MapCodec withLifecycle(final Lifecycle lifecycle) { + return new MapCodec() { + @Override + public Stream keys(final DynamicOps ops) { + return MapCodec.this.keys(ops); + } + + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return MapCodec.this.decode(ops, input).setLifecycle(lifecycle); + } + + @Override + public RecordBuilder encode(final A input, final DynamicOps ops, final RecordBuilder prefix) { + return MapCodec.this.encode(input, ops, prefix).setLifecycle(lifecycle); + } + + @Override + public String toString() { + return MapCodec.this.toString(); + } + }; + } + + public static final class MapCodecCodec implements Codec { + private final MapCodec codec; + + public MapCodecCodec(final MapCodec codec) { + this.codec = codec; + } + + public MapCodec codec() { + return codec; + } + + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return codec.compressedDecode(ops, input).map(r -> Pair.of(r, input)); + } + + @Override + public DataResult encode(final A input, final DynamicOps ops, final T prefix) { + return codec.encode(input, ops, codec.compressedBuilder(ops)).build(prefix); + } + + @Override + public String toString() { + return codec.toString(); + } + } + + public Codec codec() { + return new MapCodecCodec<>(this); + } + + public MapCodec stable() { + return withLifecycle(Lifecycle.stable()); + } + + public MapCodec deprecated(final int since) { + return withLifecycle(Lifecycle.deprecated(since)); + } + + public MapCodec xmap(final Function to, final Function from) { + return MapCodec.of(comap(from), map(to), toString() + "[xmapped]"); + } + + public MapCodec flatXmap(final Function> to, final Function> from) { + return Codec.of(flatComap(from), flatMap(to), toString() + "[flatXmapped]"); + } + + public MapCodec dependent(final MapCodec initialInstance, final Function>> splitter, final BiFunction combiner) { + return new Dependent<>(this, initialInstance, splitter, combiner); + } + + private static class Dependent extends MapCodec { + private final MapCodec initialInstance; + private final Function>> splitter; + private final MapCodec codec; + private final BiFunction combiner; + + public Dependent(final MapCodec codec, final MapCodec initialInstance, final Function>> splitter, final BiFunction combiner) { + this.initialInstance = initialInstance; + this.splitter = splitter; + this.codec = codec; + this.combiner = combiner; + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.concat(codec.keys(ops), initialInstance.keys(ops)); + } + + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return codec.decode(ops, input).flatMap((O base) -> + splitter.apply(base).getSecond().decode(ops, input).map(e -> combiner.apply(base, e)).setLifecycle(Lifecycle.experimental()) + ); + } + + @Override + public RecordBuilder encode(final O input, final DynamicOps ops, final RecordBuilder prefix) { + codec.encode(input, ops, prefix); + final Pair> e = splitter.apply(input); + e.getSecond().encode(e.getFirst(), ops, prefix); + return prefix.setLifecycle(Lifecycle.experimental()); + } + } + + @Override + public abstract Stream keys(final DynamicOps ops); + + interface ResultFunction { + DataResult apply(final DynamicOps ops, final MapLike input, final DataResult a); + + RecordBuilder coApply(final DynamicOps ops, final A input, final RecordBuilder t); + } + + private MapCodec mapResult(final ResultFunction function) { + return new MapCodec() { + @Override + public Stream keys(final DynamicOps ops) { + return MapCodec.this.keys(ops); + } + + @Override + public RecordBuilder encode(final A input, final DynamicOps ops, final RecordBuilder prefix) { + return function.coApply(ops, input, MapCodec.this.encode(input, ops, prefix)); + } + + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return function.apply(ops, input, MapCodec.this.decode(ops, input)); + } + + @Override + public String toString() { + return MapCodec.this + "[mapResult " + function + "]"; + } + }; + } + + public MapCodec withDefault(final Consumer onError, final A value) { + return withDefault(DataFixUtils.consumerToFunction(onError), value); + } + + public MapCodec withDefault(final UnaryOperator onError, final A value) { + return mapResult(new ResultFunction() { + @Override + public DataResult apply(final DynamicOps ops, final MapLike input, final DataResult a) { + return DataResult.success(a.mapError(onError).result().orElse(value)); + } + + @Override + public RecordBuilder coApply(final DynamicOps ops, final A input, final RecordBuilder t) { + return t.mapError(onError); + } + + @Override + public String toString() { + return "WithDefault[" + onError + " " + value + "]"; + } + }); + } + + public MapCodec withDefault(final Consumer onError, final Supplier value) { + return withDefault(DataFixUtils.consumerToFunction(onError), value); + } + + public MapCodec withDefault(final UnaryOperator onError, final Supplier value) { + return mapResult(new ResultFunction() { + @Override + public DataResult apply(final DynamicOps ops, final MapLike input, final DataResult a) { + return DataResult.success(a.mapError(onError).result().orElseGet(value)); + } + + @Override + public RecordBuilder coApply(final DynamicOps ops, final A input, final RecordBuilder t) { + return t.mapError(onError); + } + + @Override + public String toString() { + return "WithDefault[" + onError + " " + value.get() + "]"; + } + }); + } + + public MapCodec withDefault(final A value) { + return mapResult(new ResultFunction() { + @Override + public DataResult apply(final DynamicOps ops, final MapLike input, final DataResult a) { + return DataResult.success(a.result().orElse(value)); + } + + @Override + public RecordBuilder coApply(final DynamicOps ops, final A input, final RecordBuilder t) { + return t; + } + + @Override + public String toString() { + return "WithDefault[" + value + "]"; + } + }); + } + + public MapCodec withDefault(final Supplier value) { + return mapResult(new ResultFunction() { + @Override + public DataResult apply(final DynamicOps ops, final MapLike input, final DataResult a) { + return DataResult.success(a.result().orElseGet(value)); + } + + @Override + public RecordBuilder coApply(final DynamicOps ops, final A input, final RecordBuilder t) { + return t; + } + + @Override + public String toString() { + return "WithDefault[" + value.get() + "]"; + } + }); + } + + static MapCodec unit(final A defaultValue) { + return unit(() -> defaultValue); + } + + static MapCodec unit(final Supplier defaultValue) { + return MapCodec.of(Encoder.empty(), Decoder.unit(defaultValue)); + } +} diff --git a/src/main/java/com/mojang/serialization/MapDecoder.java b/src/main/java/com/mojang/serialization/MapDecoder.java new file mode 100644 index 00000000..1c20bcd0 --- /dev/null +++ b/src/main/java/com/mojang/serialization/MapDecoder.java @@ -0,0 +1,151 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.mojang.datafixers.util.Pair; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public interface MapDecoder extends Keyable { + DataResult decode(DynamicOps ops, MapLike input); + + default DataResult compressedDecode(final DynamicOps ops, final T input) { + if (ops.compressMaps()) { + final Optional>> inputList = ops.getList(input).result(); + + if (!inputList.isPresent()) { + return DataResult.error("Input is not a list"); + } + + final KeyCompressor compressor = compressor(ops); + final List entries = new ArrayList<>(); + inputList.get().accept(entries::add); + + final MapLike map = new MapLike() { + @Nullable + @Override + public T get(final T key) { + return entries.get(compressor.compress(key)); + } + + @Nullable + @Override + public T get(final String key) { + return entries.get(compressor.compress(key)); + } + + @Override + public Stream> entries() { + return IntStream.range(0, entries.size()).mapToObj(i -> Pair.of(compressor.decompress(i), entries.get(i))).filter(p -> p.getSecond() != null); + } + }; + return decode(ops, map); + } + // will use the lifecycle of decode + return ops.getMap(input).setLifecycle(Lifecycle.stable()).flatMap(map -> decode(ops, map)); + } + + KeyCompressor compressor(DynamicOps ops); + + default Decoder decoder() { + return new Decoder() { + @Override + public DataResult> decode(final DynamicOps ops, final T input) { + return compressedDecode(ops, input).map(r -> Pair.of(r, input)); + } + + @Override + public String toString() { + return MapDecoder.this.toString(); + } + }; + } + + default MapDecoder flatMap(final Function> function) { + return new Implementation() { + @Override + public Stream keys(final DynamicOps ops) { + return MapDecoder.this.keys(ops); + } + + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return MapDecoder.this.decode(ops, input).flatMap(b -> function.apply(b).map(Function.identity())); + } + + @Override + public String toString() { + return MapDecoder.this.toString() + "[flatMapped]"; + } + }; + } + + default MapDecoder map(final Function function) { + return new Implementation() { + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return MapDecoder.this.decode(ops, input).map(function); + } + + @Override + public Stream keys(final DynamicOps ops) { + return MapDecoder.this.keys(ops); + } + + @Override + public String toString() { + return MapDecoder.this.toString() + "[mapped]"; + } + }; + } + + default MapDecoder ap(final MapDecoder> decoder) { + return new Implementation() { + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return MapDecoder.this.decode(ops, input).flatMap(f -> + decoder.decode(ops, input).map(e -> e.apply(f)) + ); + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.concat(MapDecoder.this.keys(ops), decoder.keys(ops)); + } + + @Override + public String toString() { + return decoder.toString() + " * " + MapDecoder.this.toString(); + } + }; + } + + default MapDecoder withLifecycle(final Lifecycle lifecycle) { + return new Implementation() { + @Override + public Stream keys(final DynamicOps ops) { + return MapDecoder.this.keys(ops); + } + + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return MapDecoder.this.decode(ops, input).setLifecycle(lifecycle); + } + + @Override + public String toString() { + return MapDecoder.this.toString(); + } + }; + } + + abstract class Implementation extends CompressorHolder implements MapDecoder { + } +} diff --git a/src/main/java/com/mojang/serialization/MapEncoder.java b/src/main/java/com/mojang/serialization/MapEncoder.java new file mode 100644 index 00000000..45dbb9ff --- /dev/null +++ b/src/main/java/com/mojang/serialization/MapEncoder.java @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Stream; + +public interface MapEncoder extends Keyable { + RecordBuilder encode(A input, DynamicOps ops, RecordBuilder prefix); + + default RecordBuilder compressedBuilder(final DynamicOps ops) { + if (ops.compressMaps()) { + return makeCompressedBuilder(ops, compressor(ops)); + } + return ops.mapBuilder(); + } + + KeyCompressor compressor(final DynamicOps ops); + + default MapEncoder comap(final Function function) { + return new Implementation() { + @Override + public RecordBuilder encode(final B input, final DynamicOps ops, final RecordBuilder prefix) { + return MapEncoder.this.encode(function.apply(input), ops, prefix); + } + + @Override + public Stream keys(final DynamicOps ops) { + return MapEncoder.this.keys(ops); + } + + @Override + public String toString() { + return MapEncoder.this.toString() + "[comapped]"; + } + }; + } + + default MapEncoder flatComap(final Function> function) { + return new Implementation() { + @Override + public Stream keys(final DynamicOps ops) { + return MapEncoder.this.keys(ops); + } + + @Override + public RecordBuilder encode(final B input, final DynamicOps ops, final RecordBuilder prefix) { + final DataResult aResult = function.apply(input); + final RecordBuilder builder = prefix.withErrorsFrom(aResult); + return aResult.map(r -> MapEncoder.this.encode(r, ops, builder)).result().orElse(builder); + } + + @Override + public String toString() { + return MapEncoder.this.toString() + "[flatComapped]"; + } + }; + } + + default Encoder encoder() { + return new Encoder() { + @Override + public DataResult encode(final A input, final DynamicOps ops, final T prefix) { + return MapEncoder.this.encode(input, ops, compressedBuilder(ops)).build(prefix); + } + + @Override + public String toString() { + return MapEncoder.this.toString(); + } + }; + } + + default MapEncoder withLifecycle(final Lifecycle lifecycle) { + return new Implementation() { + @Override + public Stream keys(final DynamicOps ops) { + return MapEncoder.this.keys(ops); + } + + @Override + public RecordBuilder encode(final A input, final DynamicOps ops, final RecordBuilder prefix) { + return MapEncoder.this.encode(input, ops, prefix).setLifecycle(lifecycle); + } + + @Override + public String toString() { + return MapEncoder.this.toString(); + } + }; + } + + abstract class Implementation extends CompressorHolder implements MapEncoder { + } + + static RecordBuilder makeCompressedBuilder(final DynamicOps ops, final KeyCompressor compressor) { + class CompressedRecordBuilder extends RecordBuilder.AbstractUniversalBuilder> { + private CompressedRecordBuilder() { + super(ops); + } + + @Override + protected List initBuilder() { + final List list = new ArrayList<>(compressor.size()); + for (int i = 0; i < compressor.size(); i++) { + list.add(null); + } + return list; + } + + @Override + protected List append(final T key, final T value, final List builder) { + builder.set(compressor.compress(key), value); + return builder; + } + + @Override + protected DataResult build(final List builder, final T prefix) { + return ops().mergeToList(prefix, builder); + } + } + + return new CompressedRecordBuilder(); + } +} diff --git a/src/main/java/com/mojang/serialization/MapLike.java b/src/main/java/com/mojang/serialization/MapLike.java new file mode 100644 index 00000000..6841cacf --- /dev/null +++ b/src/main/java/com/mojang/serialization/MapLike.java @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.mojang.datafixers.util.Pair; + +import javax.annotation.Nullable; +import java.util.Map; +import java.util.stream.Stream; + +public interface MapLike { + @Nullable + T get(final T key); + + @Nullable + T get(final String key); + + Stream> entries(); + + static MapLike forMap(final Map map, final DynamicOps ops) { + return new MapLike() { + @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 "MapLike[" + map + "]"; + } + }; + } +} diff --git a/src/main/java/com/mojang/serialization/OptionalDynamic.java b/src/main/java/com/mojang/serialization/OptionalDynamic.java new file mode 100644 index 00000000..16beb0bf --- /dev/null +++ b/src/main/java/com/mojang/serialization/OptionalDynamic.java @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.mojang.datafixers.util.Pair; + +import java.nio.ByteBuffer; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +@SuppressWarnings("unused") +public final class OptionalDynamic extends DynamicLike { + private final DataResult> delegate; + + public OptionalDynamic(final DynamicOps ops, final DataResult> delegate) { + super(ops); + this.delegate = delegate; + } + + public DataResult> get() { + return delegate; + } + + public Optional> result() { + return delegate.result(); + } + + public DataResult map(final Function, U> mapper) { + return delegate.map(mapper); + } + + public DataResult flatMap(final Function, ? extends DataResult> mapper) { + return delegate.flatMap(mapper); + } + + @Override + public DataResult asNumber() { + return flatMap(DynamicLike::asNumber); + } + + @Override + public DataResult asString() { + return flatMap(DynamicLike::asString); + } + + @Override + public DataResult>> asStreamOpt() { + return flatMap(DynamicLike::asStreamOpt); + } + + @Override + public DataResult, Dynamic>>> asMapOpt() { + return flatMap(DynamicLike::asMapOpt); + } + + @Override + public DataResult asByteBufferOpt() { + return flatMap(DynamicLike::asByteBufferOpt); + } + + @Override + public DataResult asIntStreamOpt() { + return flatMap(DynamicLike::asIntStreamOpt); + } + + @Override + public DataResult asLongStreamOpt() { + return flatMap(DynamicLike::asLongStreamOpt); + } + + @Override + public OptionalDynamic get(final String key) { + return new OptionalDynamic<>(ops, delegate.flatMap(k -> k.get(key).delegate)); + } + + @Override + public DataResult getGeneric(final T key) { + return flatMap(v -> v.getGeneric(key)); + } + + @Override + public DataResult getElement(final String key) { + return flatMap(v -> v.getElement(key)); + } + + @Override + public DataResult getElementGeneric(final T key) { + return flatMap(v -> v.getElementGeneric(key)); + } + + public Dynamic orElseEmptyMap() { + return result().orElseGet(this::emptyMap); + } + + public Dynamic orElseEmptyList() { + return result().orElseGet(this::emptyList); + } + + public DataResult into(final Function, ? extends V> action) { + return delegate.map(action); + } + + @Override + public DataResult> decode(final Decoder decoder) { + return delegate.flatMap(t -> t.decode(decoder)); + } +} diff --git a/src/main/java/com/mojang/serialization/RecordBuilder.java b/src/main/java/com/mojang/serialization/RecordBuilder.java new file mode 100644 index 00000000..7a2baa65 --- /dev/null +++ b/src/main/java/com/mojang/serialization/RecordBuilder.java @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization; + +import com.google.common.collect.ImmutableMap; + +import java.util.function.UnaryOperator; + +public interface RecordBuilder { + DynamicOps ops(); + + RecordBuilder add(T key, T value); + + RecordBuilder add(T key, DataResult value); + + RecordBuilder add(DataResult key, DataResult value); + + RecordBuilder withErrorsFrom(final DataResult result); + + RecordBuilder setLifecycle(Lifecycle lifecycle); + + RecordBuilder mapError(UnaryOperator onError); + + DataResult build(T prefix); + + default DataResult build(final DataResult prefix) { + return prefix.flatMap(this::build); + } + + default RecordBuilder add(final String key, final T value) { + return add(ops().createString(key), value); + } + + default RecordBuilder add(final String key, final DataResult value) { + return add(ops().createString(key), value); + } + + default RecordBuilder add(final String key, final E value, final Encoder encoder) { + return add(key, encoder.encodeStart(ops(), value)); + } + + abstract class AbstractBuilder implements RecordBuilder { + private final DynamicOps ops; + protected DataResult builder = DataResult.success(initBuilder(), Lifecycle.stable()); + + protected AbstractBuilder(final DynamicOps ops) { + this.ops = ops; + } + + @Override + public DynamicOps ops() { + return ops; + } + + protected abstract R initBuilder(); + + protected abstract DataResult build(final R builder, final T prefix); + + @Override + public DataResult build(final T prefix) { + final DataResult result = builder.flatMap(b -> build(b, prefix)); + builder = DataResult.success(initBuilder(), Lifecycle.stable()); + return result; + } + + @Override + public RecordBuilder withErrorsFrom(final DataResult result) { + builder = builder.flatMap(v -> result.map(r -> v)); + return this; + } + + @Override + public RecordBuilder setLifecycle(final Lifecycle lifecycle) { + builder = builder.setLifecycle(lifecycle); + return this; + } + + @Override + public RecordBuilder mapError(final UnaryOperator onError) { + builder = builder.mapError(onError); + return this; + } + } + + abstract class AbstractStringBuilder extends AbstractBuilder { + protected AbstractStringBuilder(final DynamicOps ops) { + super(ops); + } + + protected abstract R append(String key, T value, R builder); + + @Override + public RecordBuilder add(final String key, final T value) { + builder = builder.map(b -> append(key, value, b)); + return this; + } + + @Override + public RecordBuilder add(final String key, final DataResult value) { + builder = builder.apply2stable((b, v) -> append(key, v, b), value); + return this; + } + + @Override + public RecordBuilder add(final T key, final T value) { + builder = ops().getStringValue(key).flatMap(k -> { + add(k, value); + return builder; + }); + return this; + } + + @Override + public RecordBuilder add(final T key, final DataResult value) { + builder = ops().getStringValue(key).flatMap(k -> { + add(k, value); + return builder; + }); + return this; + } + + @Override + public RecordBuilder add(final DataResult key, final DataResult value) { + builder = key.flatMap(ops()::getStringValue).flatMap(k -> { + add(k, value); + return builder; + }); + return this; + } + } + + abstract class AbstractUniversalBuilder extends AbstractBuilder { + protected AbstractUniversalBuilder(final DynamicOps ops) { + super(ops); + } + + protected abstract R append(T key, T value, R builder); + + @Override + public RecordBuilder add(final T key, final T value) { + builder = builder.map(b -> append(key, value, b)); + return this; + } + + @Override + public RecordBuilder add(final T key, final DataResult value) { + builder = builder.apply2stable((b, v) -> append(key, v, b), value); + return this; + } + + @Override + public RecordBuilder add(final DataResult key, final DataResult value) { + builder = builder.ap(key.apply2stable((k, v) -> b -> append(k, v, b), value)); + return this; + } + } + + final class MapBuilder extends AbstractUniversalBuilder> { + public MapBuilder(final DynamicOps ops) { + super(ops); + } + + @Override + protected ImmutableMap.Builder initBuilder() { + return ImmutableMap.builder(); + } + + @Override + protected ImmutableMap.Builder append(final T key, final T value, final ImmutableMap.Builder builder) { + return builder.put(key, value); + } + + @Override + protected DataResult build(final ImmutableMap.Builder builder, final T prefix) { + return ops().mergeToMap(prefix, builder.build()); + } + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/BaseMapCodec.java b/src/main/java/com/mojang/serialization/codecs/BaseMapCodec.java new file mode 100644 index 00000000..883617c9 --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/BaseMapCodec.java @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.util.Pair; +import com.mojang.datafixers.util.Unit; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.MapLike; +import com.mojang.serialization.RecordBuilder; + +import java.util.Map; + +public interface BaseMapCodec { + Codec keyCodec(); + + Codec elementCodec(); + + default DataResult> decode(final DynamicOps ops, final MapLike input) { + final ImmutableMap.Builder read = ImmutableMap.builder(); + final ImmutableList.Builder> failed = ImmutableList.builder(); + + final DataResult result = input.entries().reduce( + DataResult.success(Unit.INSTANCE, Lifecycle.stable()), + (r, pair) -> { + final DataResult k = keyCodec().parse(ops, pair.getFirst()); + final DataResult v = elementCodec().parse(ops, pair.getSecond()); + + final DataResult> entry = k.apply2stable(Pair::of, v); + entry.error().ifPresent(e -> failed.add(pair)); + + return r.apply2stable((u, p) -> { + read.put(p.getFirst(), p.getSecond()); + return u; + }, entry); + }, + (r1, r2) -> r1.apply2stable((u1, u2) -> u1, r2) + ); + + final Map elements = read.build(); + final T errors = ops.createMap(failed.build().stream()); + + return result.map(unit -> elements).setPartial(elements).mapError(e -> e + " missed input: " + errors); + } + + default RecordBuilder encode(final Map input, final DynamicOps ops, final RecordBuilder prefix) { + for (final Map.Entry entry : input.entrySet()) { + prefix.add(keyCodec().encodeStart(ops, entry.getKey()), elementCodec().encodeStart(ops, entry.getValue())); + } + return prefix; + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/CompoundListCodec.java b/src/main/java/com/mojang/serialization/codecs/CompoundListCodec.java new file mode 100644 index 00000000..883c61ac --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/CompoundListCodec.java @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.util.Pair; +import com.mojang.datafixers.util.Unit; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.RecordBuilder; +import org.apache.commons.lang3.mutable.MutableObject; + +import java.util.List; +import java.util.Objects; + +public final class CompoundListCodec implements Codec>> { + private final Codec keyCodec; + private final Codec elementCodec; + + public CompoundListCodec(final Codec keyCodec, final Codec elementCodec) { + this.keyCodec = keyCodec; + this.elementCodec = elementCodec; + } + + @Override + public DataResult>, T>> decode(final DynamicOps ops, final T input) { + return ops.getMapEntries(input).flatMap(map -> { + final ImmutableList.Builder> read = ImmutableList.builder(); + final ImmutableMap.Builder failed = ImmutableMap.builder(); + + // TODO: AtomicReference.getPlain/setPlain in java9+ + final MutableObject> result = new MutableObject<>(DataResult.success(Unit.INSTANCE, Lifecycle.experimental())); + + map.accept((key, value) -> { + final DataResult k = keyCodec.parse(ops, key); + final DataResult v = elementCodec.parse(ops, value); + + final DataResult> readEntry = k.apply2stable(Pair::new, v); + + readEntry.error().ifPresent(e -> failed.put(key, value)); + + result.setValue(result.getValue().apply2stable((u, e) -> { + read.add(e); + return u; + }, readEntry)); + }); + + final ImmutableList> elements = read.build(); + final T errors = ops.createMap(failed.build()); + + final Pair>, T> pair = Pair.of(elements, errors); + + return result.getValue().map(unit -> pair).setPartial(pair); + }); + } + + @Override + public DataResult encode(final List> input, final DynamicOps ops, final T prefix) { + final RecordBuilder builder = ops.mapBuilder(); + + for (final Pair pair : input) { + builder.add(keyCodec.encodeStart(ops, pair.getFirst()), elementCodec.encodeStart(ops, pair.getSecond())); + } + + return builder.build(prefix); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final CompoundListCodec that = (CompoundListCodec) o; + return Objects.equals(keyCodec, that.keyCodec) && Objects.equals(elementCodec, that.elementCodec); + } + + @Override + public int hashCode() { + return Objects.hash(keyCodec, elementCodec); + } + + @Override + public String toString() { + return "CompoundListCodec[" + keyCodec + " -> " + elementCodec + ']'; + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/EitherCodec.java b/src/main/java/com/mojang/serialization/codecs/EitherCodec.java new file mode 100644 index 00000000..544e49c2 --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/EitherCodec.java @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; + +import java.util.Objects; + +public final class EitherCodec implements Codec> { + private final Codec first; + private final Codec second; + + public EitherCodec(final Codec first, final Codec second) { + this.first = first; + this.second = second; + } + + @Override + public DataResult, T>> decode(final DynamicOps ops, final T input) { + final DataResult, T>> firstRead = first.decode(ops, input).map(vo -> vo.mapFirst(Either::left)); + if (firstRead.result().isPresent()) { + return firstRead; + } + return second.decode(ops, input).map(vo -> vo.mapFirst(Either::right)); + } + + @Override + public DataResult encode(final Either input, final DynamicOps ops, final T prefix) { + return input.map( + value1 -> first.encode(value1, ops, prefix), + value2 -> second.encode(value2, ops, prefix) + ); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final EitherCodec eitherCodec = ((EitherCodec) o); + return Objects.equals(first, eitherCodec.first) && Objects.equals(second, eitherCodec.second); + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } + + @Override + public String toString() { + return "EitherCodec[" + first + ", " + second + ']'; + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/EitherMapCodec.java b/src/main/java/com/mojang/serialization/codecs/EitherMapCodec.java new file mode 100644 index 00000000..da2a0298 --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/EitherMapCodec.java @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.MapLike; +import com.mojang.serialization.RecordBuilder; + +import java.util.Objects; +import java.util.stream.Stream; + +public final class EitherMapCodec extends MapCodec> { + private final MapCodec first; + private final MapCodec second; + + public EitherMapCodec(final MapCodec first, final MapCodec second) { + this.first = first; + this.second = second; + } + + @Override + public DataResult> decode(final DynamicOps ops, final MapLike input) { + final DataResult> firstRead = first.decode(ops, input).map(Either::left); + if (firstRead.result().isPresent()) { + return firstRead; + } + return second.decode(ops, input).map(Either::right); + } + + @Override + public RecordBuilder encode(final Either input, final DynamicOps ops, final RecordBuilder prefix) { + return input.map( + value1 -> first.encode(value1, ops, prefix), + value2 -> second.encode(value2, ops, prefix) + ); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final EitherMapCodec eitherCodec = ((EitherMapCodec) o); + return Objects.equals(first, eitherCodec.first) && Objects.equals(second, eitherCodec.second); + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } + + @Override + public String toString() { + return "EitherMapCodec[" + first + ", " + second + ']'; + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.concat(first.keys(ops), second.keys(ops)); + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/FieldDecoder.java b/src/main/java/com/mojang/serialization/codecs/FieldDecoder.java new file mode 100644 index 00000000..6c0b9d4e --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/FieldDecoder.java @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.mojang.serialization.DataResult; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.MapDecoder; +import com.mojang.serialization.MapLike; + +import java.util.Objects; +import java.util.stream.Stream; + +public final class FieldDecoder extends MapDecoder.Implementation { + protected final String name; + private final Decoder elementCodec; + + public FieldDecoder(final String name, final Decoder elementCodec) { + this.name = name; + this.elementCodec = elementCodec; + } + + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + final T value = input.get(name); + if (value == null) { + return DataResult.error("No key " + name + " in " + input); + } + return elementCodec.parse(ops, value); + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.of(ops.createString(name)); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final FieldDecoder that = (FieldDecoder) o; + return Objects.equals(name, that.name) && Objects.equals(elementCodec, that.elementCodec); + } + + @Override + public int hashCode() { + return Objects.hash(name, elementCodec); + } + + @Override + public String toString() { + return "FieldDecoder[" + name + ": " + elementCodec + ']'; + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/FieldEncoder.java b/src/main/java/com/mojang/serialization/codecs/FieldEncoder.java new file mode 100644 index 00000000..d5a491fe --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/FieldEncoder.java @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Encoder; +import com.mojang.serialization.MapEncoder; +import com.mojang.serialization.RecordBuilder; + +import java.util.Objects; +import java.util.stream.Stream; + +public class FieldEncoder extends MapEncoder.Implementation { + private final String name; + private final Encoder elementCodec; + + public FieldEncoder(final String name, final Encoder elementCodec) { + this.name = name; + this.elementCodec = elementCodec; + } + + @Override + public RecordBuilder encode(final A input, final DynamicOps ops, final RecordBuilder prefix) { + return prefix.add(name, elementCodec.encodeStart(ops, input)); + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.of(ops.createString(name)); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final FieldEncoder that = (FieldEncoder) o; + return Objects.equals(name, that.name) && Objects.equals(elementCodec, that.elementCodec); + } + + @Override + public int hashCode() { + return Objects.hash(name, elementCodec); + } + + @Override + public String toString() { + return "FieldEncoder[" + name + ": " + elementCodec + ']'; + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/KeyDispatchCodec.java b/src/main/java/com/mojang/serialization/codecs/KeyDispatchCodec.java new file mode 100644 index 00000000..ba79d86c --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/KeyDispatchCodec.java @@ -0,0 +1,112 @@ +package com.mojang.serialization.codecs; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Encoder; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.MapLike; +import com.mojang.serialization.RecordBuilder; + +import java.util.function.Function; +import java.util.stream.Stream; + +public class KeyDispatchCodec extends MapCodec { + private final String typeKey; + private final Codec keyCodec; + private final String valueKey = "value"; + private final Function> type; + private final Function>> decoder; + private final Function>> encoder; + + public KeyDispatchCodec(final String typeKey, final Codec keyCodec, final Function> type, final Function>> decoder, final Function>> encoder) { + this.typeKey = typeKey; + this.keyCodec = keyCodec; + this.type = type; + this.decoder = decoder; + this.encoder = encoder; + } + + /** + * Assumes codec(type(V)) is Codec + */ + public KeyDispatchCodec(final String typeKey, final Codec keyCodec, final Function> type, final Function>> codec) { + this(typeKey, keyCodec, type, codec, v -> getCodec(type, codec, v)); + } + + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + final T elementName = input.get(typeKey); + if (elementName == null) { + return DataResult.error("Input does not contain a key [" + typeKey + "]: " + input); + } + + return keyCodec.decode(ops, elementName).flatMap(type -> { + final DataResult> elementDecoder = decoder.apply(type.getFirst()); + return elementDecoder.flatMap(c -> { + if (ops.compressMaps()) { + final T value = input.get(ops.createString(valueKey)); + if (value == null) { + return DataResult.error("Input does not have a \"value\" entry: " + input); + } + return c.parse(ops, value).map(Function.identity()); + } + if (c instanceof MapCodecCodec) { + return ((MapCodecCodec) c).codec().decode(ops, input).map(Function.identity()); + } + return c.decode(ops, ops.createMap(input.entries())).map(Pair::getFirst); + }); + }); + } + + @Override + public RecordBuilder encode(final V input, final DynamicOps ops, final RecordBuilder prefix) { + final DataResult> elementEncoder = encoder.apply(input); + final RecordBuilder builder = prefix.withErrorsFrom(elementEncoder); + if (!elementEncoder.result().isPresent()) { + return builder; + } + + final Encoder c = elementEncoder.result().get(); + if (ops.compressMaps()) { + return prefix + .add(typeKey, type.apply(input).flatMap(t -> keyCodec.encodeStart(ops, t))) + .add(valueKey, c.encodeStart(ops, input)); + } + if (c instanceof MapCodecCodec) { + return ((MapCodecCodec) c).codec().encode(input, ops, prefix) + .add(typeKey, type.apply(input).flatMap(t -> keyCodec.encodeStart(ops, t))); + } + + final T typeString = ops.createString(typeKey); + + final DataResult> element = c.encodeStart(ops, input).flatMap(ops::getMap); + return element.map(map -> { + map.entries().forEach(pair -> { + if (pair.getFirst().equals(typeString)) { + prefix.add(typeString, type.apply(input).flatMap(t -> keyCodec.encodeStart(ops, t))); + } else { + prefix.add(pair.getFirst(), pair.getSecond()); + } + }); + return prefix; + }).result().orElseGet(() -> prefix.withErrorsFrom(element)); + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.of(typeKey, valueKey).map(ops::createString); + } + + @SuppressWarnings("unchecked") + private static DataResult> getCodec(final Function> type, final Function>> encoder, final V input) { + return type.apply(input).>flatMap(k -> encoder.apply(k).map(Function.identity())).map(c -> ((Encoder) c)); + } + + @Override + public String toString() { + return "KeyDispatchCodec[" + keyCodec.toString() + " " + type + " " + decoder + "]"; + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/ListCodec.java b/src/main/java/com/mojang/serialization/codecs/ListCodec.java new file mode 100644 index 00000000..85152819 --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/ListCodec.java @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.google.common.collect.ImmutableList; +import com.mojang.datafixers.util.Pair; +import com.mojang.datafixers.util.Unit; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.ListBuilder; +import org.apache.commons.lang3.mutable.MutableObject; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +public final class ListCodec implements Codec> { + private final Codec elementCodec; + + public ListCodec(final Codec elementCodec) { + this.elementCodec = elementCodec; + } + + @Override + public DataResult encode(final List input, final DynamicOps ops, final T prefix) { + final ListBuilder builder = ops.listBuilder(); + + for (final A a : input) { + builder.add(elementCodec.encodeStart(ops, a)); + } + + return builder.build(prefix); + } + + @Override + public DataResult, T>> decode(final DynamicOps ops, final T input) { + return ops.getList(input).setLifecycle(Lifecycle.stable()).flatMap(stream -> { + final ImmutableList.Builder read = ImmutableList.builder(); + final Stream.Builder failed = Stream.builder(); + // TODO: AtomicReference.getPlain/setPlain in java9+ + final MutableObject> result = new MutableObject<>(DataResult.success(Unit.INSTANCE, Lifecycle.stable())); + + stream.accept(t -> { + final DataResult> element = elementCodec.decode(ops, t); + element.error().ifPresent(e -> failed.add(t)); + result.setValue(result.getValue().apply2stable((r, v) -> { + read.add(v.getFirst()); + return r; + }, element)); + }); + + final ImmutableList elements = read.build(); + final T errors = ops.createList(failed.build()); + + final Pair, T> pair = Pair.of(elements, errors); + + return result.getValue().map(unit -> pair).setPartial(pair); + }); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ListCodec listCodec = (ListCodec) o; + return Objects.equals(elementCodec, listCodec.elementCodec); + } + + @Override + public int hashCode() { + return Objects.hash(elementCodec); + } + + @Override + public String toString() { + return "ListCodec[" + elementCodec + ']'; + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/OptionalFieldCodec.java b/src/main/java/com/mojang/serialization/codecs/OptionalFieldCodec.java new file mode 100644 index 00000000..03af588d --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/OptionalFieldCodec.java @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.MapLike; +import com.mojang.serialization.RecordBuilder; + +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +/** Optimization of `Codec.either(someCodec.field(name), Codec.EMPTY)` */ +public class OptionalFieldCodec extends MapCodec> { + private final String name; + private final Codec elementCodec; + + public OptionalFieldCodec(final String name, final Codec elementCodec) { + this.name = name; + this.elementCodec = elementCodec; + } + + @Override + public DataResult> decode(final DynamicOps ops, final MapLike input) { + final T value = input.get(name); + if (value == null) { + return DataResult.success(Optional.empty()); + } + final DataResult parsed = elementCodec.parse(ops, value); + if (parsed.result().isPresent()) { + return parsed.map(Optional::of); + } + return DataResult.success(Optional.empty()); + } + + @Override + public RecordBuilder encode(final Optional input, final DynamicOps ops, final RecordBuilder prefix) { + if (input.isPresent()) { + return prefix.add(name, elementCodec.encodeStart(ops, input.get())); + } + return prefix; + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.of(ops.createString(name)); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final OptionalFieldCodec that = (OptionalFieldCodec) o; + return Objects.equals(name, that.name) && Objects.equals(elementCodec, that.elementCodec); + } + + @Override + public int hashCode() { + return Objects.hash(name, elementCodec); + } + + @Override + public String toString() { + return "OptionalFieldCodec[" + name + ": " + elementCodec + ']'; + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/PairCodec.java b/src/main/java/com/mojang/serialization/codecs/PairCodec.java new file mode 100644 index 00000000..b03cddaa --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/PairCodec.java @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; + +import java.util.Objects; + +public final class PairCodec implements Codec> { + private final Codec first; + private final Codec second; + + public PairCodec(final Codec first, final Codec second) { + this.first = first; + this.second = second; + } + + @Override + public DataResult, T>> decode(final DynamicOps ops, final T input) { + return first.decode(ops, input).flatMap(p1 -> + second.decode(ops, p1.getSecond()).map(p2 -> + Pair.of(Pair.of(p1.getFirst(), p2.getFirst()), p2.getSecond()) + ) + ); + } + + @Override + public DataResult encode(final Pair value, final DynamicOps ops, final T rest) { + return second.encode(value.getSecond(), ops, rest).flatMap(f -> first.encode(value.getFirst(), ops, f)); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final PairCodec pairCodec = (PairCodec) o; + return Objects.equals(first, pairCodec.first) && Objects.equals(second, pairCodec.second); + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } + + @Override + public String toString() { + return "PairCodec[" + first + ", " + second + ']'; + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/PairMapCodec.java b/src/main/java/com/mojang/serialization/codecs/PairMapCodec.java new file mode 100644 index 00000000..65a84c16 --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/PairMapCodec.java @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.MapLike; +import com.mojang.serialization.RecordBuilder; + +import java.util.Objects; +import java.util.stream.Stream; + +public final class PairMapCodec extends MapCodec> { + private final MapCodec first; + private final MapCodec second; + + public PairMapCodec(final MapCodec first, final MapCodec second) { + this.first = first; + this.second = second; + } + + @Override + public DataResult> decode(final DynamicOps ops, final MapLike input) { + return first.decode(ops, input).flatMap(p1 -> + second.decode(ops, input).map(p2 -> + Pair.of(p1, p2) + ) + ); + } + + @Override + public RecordBuilder encode(final Pair input, final DynamicOps ops, final RecordBuilder prefix) { + return first.encode(input.getFirst(), ops, second.encode(input.getSecond(), ops, prefix)); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final PairMapCodec pairCodec = (PairMapCodec) o; + return Objects.equals(first, pairCodec.first) && Objects.equals(second, pairCodec.second); + } + + @Override + public int hashCode() { + return Objects.hash(first, second); + } + + @Override + public String toString() { + return "PairMapCodec[" + first + ", " + second + ']'; + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.concat(first.keys(ops), second.keys(ops)); + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/PrimitiveCodec.java b/src/main/java/com/mojang/serialization/codecs/PrimitiveCodec.java new file mode 100644 index 00000000..78671fb7 --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/PrimitiveCodec.java @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; + +public interface PrimitiveCodec extends Codec { + DataResult read(final DynamicOps ops, final T input); + + T write(final DynamicOps ops, final A value); + + @Override + default DataResult> decode(final DynamicOps ops, final T input) { + return read(ops, input).map(r -> Pair.of(r, ops.empty())); + } + + @Override + default DataResult encode(final A input, final DynamicOps ops, final T prefix) { + return ops.mergeToPrimitive(prefix, write(ops, input)); + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/RecordCodecBuilder.java b/src/main/java/com/mojang/serialization/codecs/RecordCodecBuilder.java new file mode 100644 index 00000000..72cb7812 --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/RecordCodecBuilder.java @@ -0,0 +1,460 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.mojang.datafixers.kinds.App; +import com.mojang.datafixers.kinds.Applicative; +import com.mojang.datafixers.kinds.K1; +import com.mojang.datafixers.util.Function3; +import com.mojang.datafixers.util.Function4; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Encoder; +import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.MapDecoder; +import com.mojang.serialization.MapEncoder; +import com.mojang.serialization.MapLike; +import com.mojang.serialization.RecordBuilder; + +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Stream; + +public final class RecordCodecBuilder implements App, F> { + public static final class Mu implements K1 {} + + public static RecordCodecBuilder unbox(final App, F> box) { + return ((RecordCodecBuilder) box); + } + + private final Function getter; + private final Function> encoder; + private final MapDecoder decoder; + + private RecordCodecBuilder(final Function getter, final Function> encoder, final MapDecoder decoder) { + this.getter = getter; + this.encoder = encoder; + this.decoder = decoder; + } + + public static Instance instance() { + return new Instance<>(); + } + + public static RecordCodecBuilder of(final Function getter, final String name, final Codec fieldCodec) { + return of(getter, fieldCodec.fieldOf(name)); + } + + public static RecordCodecBuilder of(final Function getter, final MapCodec codec) { + return new RecordCodecBuilder<>(getter, o -> codec, codec); + } + + public static RecordCodecBuilder point(final F instance) { + return new RecordCodecBuilder<>(o -> instance, o -> Encoder.empty(), Decoder.unit(instance)); + } + + public static RecordCodecBuilder stable(final F instance) { + return point(instance, Lifecycle.stable()); + } + + public static RecordCodecBuilder deprecated(final F instance, final int since) { + return point(instance, Lifecycle.deprecated(since)); + } + + public static RecordCodecBuilder point(final F instance, final Lifecycle lifecycle) { + return new RecordCodecBuilder<>(o -> instance, o -> Encoder.empty().withLifecycle(lifecycle), Decoder.unit(instance).withLifecycle(lifecycle)); + } + + public static Codec create(final Function, ? extends App, O>> builder) { + return build(builder.apply(instance())).codec(); + } + + public static MapCodec mapCodec(final Function, ? extends App, O>> builder) { + return build(builder.apply(instance())); + } + + public RecordCodecBuilder dependent(final Function getter, final MapEncoder encoder, final Function> decoderGetter) { + return new RecordCodecBuilder<>( + getter, + o -> encoder, + new MapDecoder.Implementation() { + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return decoder.decode(ops, input).map(decoderGetter).flatMap(decoder1 -> decoder1.decode(ops, input).map(Function.identity())); + } + + @Override + public Stream keys(final DynamicOps ops) { + return encoder.keys(ops); + } + + @Override + public String toString() { + return "Dependent[" + encoder + "]"; + } + } + ); + } + + public static MapCodec build(final App, O> builderBox) { + final RecordCodecBuilder builder = unbox(builderBox); + return new MapCodec() { + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return builder.decoder.decode(ops, input); + } + + @Override + public RecordBuilder encode(final O input, final DynamicOps ops, final RecordBuilder prefix) { + return builder.encoder.apply(input).encode(input, ops, prefix); + } + + @Override + public Stream keys(final DynamicOps ops) { + return builder.decoder.keys(ops); + } + + @Override + public String toString() { + return "RecordCodec[" + builder.decoder + "]"; + } + }; + } + + public static final class Instance implements Applicative, Instance.Mu> { + private static final class Mu implements Applicative.Mu {} + + public App, A> stable(final A a) { + return RecordCodecBuilder.stable(a); + } + + public App, A> deprecated(final A a, final int since) { + return RecordCodecBuilder.deprecated(a, since); + } + + public App, A> point(final A a, final Lifecycle lifecycle) { + return RecordCodecBuilder.point(a, lifecycle); + } + + @Override + public App, A> point(final A a) { + return RecordCodecBuilder.point(a); + } + + @Override + public Function, A>, App, R>> lift1(final App, Function> function) { + return fa -> { + final RecordCodecBuilder> f = unbox(function); + final RecordCodecBuilder a = unbox(fa); + + return new RecordCodecBuilder<>( + o -> f.getter.apply(o).apply(a.getter.apply(o)), + o -> { + final MapEncoder> fEnc = f.encoder.apply(o); + final MapEncoder aEnc = a.encoder.apply(o); + final A aFromO = a.getter.apply(o); + + return new MapEncoder.Implementation() { + @Override + public RecordBuilder encode(final R input, final DynamicOps ops, final RecordBuilder prefix) { + aEnc.encode(aFromO, ops, prefix); + fEnc.encode(a1 -> input, ops, prefix); + return prefix; + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.concat(aEnc.keys(ops), fEnc.keys(ops)); + } + + @Override + public String toString() { + return fEnc + " * " + aEnc; + } + }; + }, + + new MapDecoder.Implementation() { + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return a.decoder.decode(ops, input).flatMap(ar -> + f.decoder.decode(ops, input).map(fr -> + fr.apply(ar) + ) + ); + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.concat(a.decoder.keys(ops), f.decoder.keys(ops)); + } + + @Override + public String toString() { + return f.decoder + " * " + a.decoder; + } + } + ); + }; + } + + @Override + public App, R> ap2(final App, BiFunction> func, final App, A> a, final App, B> b) { + final RecordCodecBuilder> function = unbox(func); + final RecordCodecBuilder fa = unbox(a); + final RecordCodecBuilder fb = unbox(b); + + return new RecordCodecBuilder<>( + o -> function.getter.apply(o).apply(fa.getter.apply(o), fb.getter.apply(o)), + o -> { + final MapEncoder> fEncoder = function.encoder.apply(o); + final MapEncoder aEncoder = fa.encoder.apply(o); + final A aFromO = fa.getter.apply(o); + final MapEncoder bEncoder = fb.encoder.apply(o); + final B bFromO = fb.getter.apply(o); + + return new MapEncoder.Implementation() { + @Override + public RecordBuilder encode(final R input, final DynamicOps ops, final RecordBuilder prefix) { + aEncoder.encode(aFromO, ops, prefix); + bEncoder.encode(bFromO, ops, prefix); + fEncoder.encode((a1, b1) -> input, ops, prefix); + return prefix; + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.of( + fEncoder.keys(ops), + aEncoder.keys(ops), + bEncoder.keys(ops) + ).flatMap(Function.identity()); + } + + @Override + public String toString() { + return fEncoder + " * " + aEncoder + " * " + bEncoder; + } + }; + }, + new MapDecoder.Implementation() { + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return DataResult.unbox(DataResult.instance().ap2( + function.decoder.decode(ops, input), + fa.decoder.decode(ops, input), + fb.decoder.decode(ops, input) + )); + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.of( + function.decoder.keys(ops), + fa.decoder.keys(ops), + fb.decoder.keys(ops) + ).flatMap(Function.identity()); + } + + @Override + public String toString() { + return function.decoder + " * " + fa.decoder + " * " + fb.decoder; + } + } + ); + } + + @Override + public App, R> ap3(final App, Function3> func, final App, T1> t1, final App, T2> t2, final App, T3> t3) { + final RecordCodecBuilder> function = unbox(func); + final RecordCodecBuilder f1 = unbox(t1); + final RecordCodecBuilder f2 = unbox(t2); + final RecordCodecBuilder f3 = unbox(t3); + + return new RecordCodecBuilder<>( + o -> function.getter.apply(o).apply( + f1.getter.apply(o), + f2.getter.apply(o), + f3.getter.apply(o) + ), + o -> { + final MapEncoder> fEncoder = function.encoder.apply(o); + final MapEncoder e1 = f1.encoder.apply(o); + final T1 v1 = f1.getter.apply(o); + final MapEncoder e2 = f2.encoder.apply(o); + final T2 v2 = f2.getter.apply(o); + final MapEncoder e3 = f3.encoder.apply(o); + final T3 v3 = f3.getter.apply(o); + + return new MapEncoder.Implementation() { + @Override + public RecordBuilder encode(final R input, final DynamicOps ops, final RecordBuilder prefix) { + e1.encode(v1, ops, prefix); + e2.encode(v2, ops, prefix); + e3.encode(v3, ops, prefix); + fEncoder.encode((t1, t2, t3) -> input, ops, prefix); + return prefix; + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.of( + fEncoder.keys(ops), + e1.keys(ops), + e2.keys(ops), + e3.keys(ops) + ).flatMap(Function.identity()); + } + + @Override + public String toString() { + return fEncoder + " * " + e1 + " * " + e2 + " * " + e3; + } + }; + }, + new MapDecoder.Implementation() { + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return DataResult.unbox(DataResult.instance().ap3( + function.decoder.decode(ops, input), + f1.decoder.decode(ops, input), + f2.decoder.decode(ops, input), + f3.decoder.decode(ops, input) + )); + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.of( + function.decoder.keys(ops), + f1.decoder.keys(ops), + f2.decoder.keys(ops), + f3.decoder.keys(ops) + ).flatMap(Function.identity()); + } + + @Override + public String toString() { + return function.decoder + " * " + f1.decoder + " * " + f2.decoder + " * " + f3.decoder; + } + } + ); + } + + @Override + public App, R> ap4(final App, Function4> func, final App, T1> t1, final App, T2> t2, final App, T3> t3, final App, T4> t4) { + final RecordCodecBuilder> function = unbox(func); + final RecordCodecBuilder f1 = unbox(t1); + final RecordCodecBuilder f2 = unbox(t2); + final RecordCodecBuilder f3 = unbox(t3); + final RecordCodecBuilder f4 = unbox(t4); + + return new RecordCodecBuilder<>( + o -> function.getter.apply(o).apply( + f1.getter.apply(o), + f2.getter.apply(o), + f3.getter.apply(o), + f4.getter.apply(o) + ), + o -> { + final MapEncoder> fEncoder = function.encoder.apply(o); + final MapEncoder e1 = f1.encoder.apply(o); + final T1 v1 = f1.getter.apply(o); + final MapEncoder e2 = f2.encoder.apply(o); + final T2 v2 = f2.getter.apply(o); + final MapEncoder e3 = f3.encoder.apply(o); + final T3 v3 = f3.getter.apply(o); + final MapEncoder e4 = f4.encoder.apply(o); + final T4 v4 = f4.getter.apply(o); + + return new MapEncoder.Implementation() { + @Override + public RecordBuilder encode(final R input, final DynamicOps ops, final RecordBuilder prefix) { + e1.encode(v1, ops, prefix); + e2.encode(v2, ops, prefix); + e3.encode(v3, ops, prefix); + e4.encode(v4, ops, prefix); + fEncoder.encode((t1, t2, t3, t4) -> input, ops, prefix); + return prefix; + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.of( + fEncoder.keys(ops), + e1.keys(ops), + e2.keys(ops), + e3.keys(ops), + e4.keys(ops) + ).flatMap(Function.identity()); + } + + @Override + public String toString() { + return fEncoder + " * " + e1 + " * " + e2 + " * " + e3 + " * " + e4; + } + }; + }, + new MapDecoder.Implementation() { + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + return DataResult.unbox(DataResult.instance().ap4( + function.decoder.decode(ops, input), + f1.decoder.decode(ops, input), + f2.decoder.decode(ops, input), + f3.decoder.decode(ops, input), + f4.decoder.decode(ops, input) + )); + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.of( + function.decoder.keys(ops), + f1.decoder.keys(ops), + f2.decoder.keys(ops), + f3.decoder.keys(ops), + f4.decoder.keys(ops) + ).flatMap(Function.identity()); + } + + @Override + public String toString() { + return function.decoder + " * " + f1.decoder + " * " + f2.decoder + " * " + f3.decoder + " * " + f4.decoder; + } + } + ); + } + + @Override + public App, R> map(final Function func, final App, T> ts) { + final RecordCodecBuilder unbox = unbox(ts); + final Function getter = unbox.getter; + return new RecordCodecBuilder<>( + getter.andThen(func), + o -> new MapEncoder.Implementation() { + private final MapEncoder encoder = unbox.encoder.apply(o); + + @Override + public RecordBuilder encode(final R input, final DynamicOps ops, final RecordBuilder prefix) { + return encoder.encode(getter.apply(o), ops, prefix); + } + + @Override + public Stream keys(final DynamicOps ops) { + return encoder.keys(ops); + } + + @Override + public String toString() { + return encoder + "[mapped]"; + } + }, + unbox.decoder.map(func) + ); + } + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/SimpleMapCodec.java b/src/main/java/com/mojang/serialization/codecs/SimpleMapCodec.java new file mode 100644 index 00000000..a916751f --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/SimpleMapCodec.java @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Keyable; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.MapLike; +import com.mojang.serialization.RecordBuilder; + +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * Key and value decoded independently, statically known set of keys + */ +public final class SimpleMapCodec extends MapCodec> implements BaseMapCodec { + private final Codec keyCodec; + private final Codec elementCodec; + private final Keyable keys; + + public SimpleMapCodec(final Codec keyCodec, final Codec elementCodec, final Keyable keys) { + this.keyCodec = keyCodec; + this.elementCodec = elementCodec; + this.keys = keys; + } + + @Override + public Codec keyCodec() { + return keyCodec; + } + + @Override + public Codec elementCodec() { + return elementCodec; + } + + @Override + public Stream keys(final DynamicOps ops) { + return keys.keys(ops); + } + + @Override + public DataResult> decode(final DynamicOps ops, final MapLike input) { + return BaseMapCodec.super.decode(ops, input); + } + + @Override + public RecordBuilder encode(final Map input, final DynamicOps ops, final RecordBuilder prefix) { + return BaseMapCodec.super.encode(input, ops, prefix); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final SimpleMapCodec that = (SimpleMapCodec) o; + return Objects.equals(keyCodec, that.keyCodec) && Objects.equals(elementCodec, that.elementCodec); + } + + @Override + public int hashCode() { + return Objects.hash(keyCodec, elementCodec); + } + + @Override + public String toString() { + return "SimpleMapCodec[" + keyCodec + " -> " + elementCodec + ']'; + } +} diff --git a/src/main/java/com/mojang/serialization/codecs/UnboundedMapCodec.java b/src/main/java/com/mojang/serialization/codecs/UnboundedMapCodec.java new file mode 100644 index 00000000..0e7cf972 --- /dev/null +++ b/src/main/java/com/mojang/serialization/codecs/UnboundedMapCodec.java @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.serialization.codecs; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Lifecycle; + +import java.util.Map; +import java.util.Objects; + +/** + * Key and value decoded independently, unknown set of keys + */ +public final class UnboundedMapCodec implements BaseMapCodec, Codec> { + private final Codec keyCodec; + private final Codec elementCodec; + + public UnboundedMapCodec(final Codec keyCodec, final Codec elementCodec) { + this.keyCodec = keyCodec; + this.elementCodec = elementCodec; + } + + @Override + public Codec keyCodec() { + return keyCodec; + } + + @Override + public Codec elementCodec() { + return elementCodec; + } + + @Override + public DataResult, T>> decode(final DynamicOps ops, final T input) { + return ops.getMap(input).setLifecycle(Lifecycle.stable()).flatMap(map -> decode(ops, map)).map(r -> Pair.of(r, input)); + } + + @Override + public DataResult encode(final Map input, final DynamicOps ops, final T prefix) { + return encode(input, ops, ops.mapBuilder()).build(prefix); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final UnboundedMapCodec that = (UnboundedMapCodec) o; + return Objects.equals(keyCodec, that.keyCodec) && Objects.equals(elementCodec, that.elementCodec); + } + + @Override + public int hashCode() { + return Objects.hash(keyCodec, elementCodec); + } + + @Override + public String toString() { + return "UnboundedMapCodec[" + keyCodec + " -> " + elementCodec + ']'; + } +} diff --git a/src/test/java/com/mojang/serialization/RoundtripTest.java b/src/test/java/com/mojang/serialization/RoundtripTest.java new file mode 100644 index 00000000..6b529d16 --- /dev/null +++ b/src/test/java/com/mojang/serialization/RoundtripTest.java @@ -0,0 +1,284 @@ +package com.mojang.serialization; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Random; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.junit.Assert.assertEquals; + +public class RoundtripTest { + // Constructors, equals and hashcode are auto-generated + // TODO: switch to records in java 14+ + + private enum Day { + TUESDAY("tuesday", TuesdayData.CODEC), + WEDNESDAY("wednesday", WednesdayData.CODEC), + SUNDAY("sunday", SundayData.CODEC), + ; + + private static final Map BY_NAME = Arrays.stream(values()).collect(Collectors.toMap(v -> v.name, Function.identity())); + public static final Codec CODEC = Codec.STRING.comapFlatMap(DataResult.partialGet(BY_NAME::get, () -> "unknown day"), d -> d.name); + + private final String name; + private final Codec codec; + + Day(final String name, final Codec codec) { + this.name = name; + this.codec = codec.fieldOf("data").codec(); + } + + public Codec codec() { + return codec; + } + } + + interface DayData { + Codec CODEC = Day.CODEC.dispatch(DayData::type, Day::codec); + Day type(); + } + + private static final class TuesdayData implements DayData { + public static final Codec CODEC = Codec.INT.xmap(TuesdayData::new, d -> d.x); + + private final int x; + + private TuesdayData(final int x) { + this.x = x; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final TuesdayData that = (TuesdayData) o; + return x == that.x; + } + + @Override + public int hashCode() { + return Objects.hash(x); + } + + @Override + public Day type() { + return Day.TUESDAY; + } + } + + private static final class WednesdayData implements DayData { + public static final Codec CODEC = Codec.STRING.xmap(WednesdayData::new, d -> d.y); + + private final String y; + + private WednesdayData(final String y) { + this.y = y; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final WednesdayData that = (WednesdayData) o; + return Objects.equals(y, that.y); + } + + @Override + public int hashCode() { + return Objects.hash(y); + } + + @Override + public Day type() { + return Day.WEDNESDAY; + } + } + + private static final class SundayData implements DayData { + public static final Codec CODEC = Codec.FLOAT.xmap(SundayData::new, d -> d.z); + + private final float z; + + private SundayData(final float z) { + this.z = z; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final SundayData that = (SundayData) o; + return Float.compare(that.z, z) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(z); + } + + @Override + public Day type() { + return Day.SUNDAY; + } + } + + private static final class TestData { + public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( + Codec.FLOAT.fieldOf("a").forGetter(d -> d.a), + Codec.DOUBLE.fieldOf("b").forGetter(d -> d.b), + Codec.BYTE.fieldOf("c").forGetter(d -> d.c), + Codec.SHORT.fieldOf("d").forGetter(d -> d.d), + Codec.INT.fieldOf("e").forGetter(d -> d.e), + Codec.LONG.fieldOf("f").forGetter(d -> d.f), + Codec.BOOL.fieldOf("g").forGetter(d -> d.g), + Codec.STRING.fieldOf("h").forGetter(d -> d.h), + Codec.STRING.listOf().fieldOf("i").forGetter(d -> d.i), + Codec.unboundedMap(Codec.STRING, Codec.STRING).fieldOf("j").forGetter(d -> d.j), + Codec.compoundList(Codec.STRING, Codec.STRING).fieldOf("k").forGetter(d -> d.k), + DayData.CODEC.fieldOf("day_data").forGetter(d -> d.dayData) + ).apply(i, TestData::new)); + + private final float a; + private final double b; + private final byte c; + private final short d; + private final int e; + private final long f; + private final boolean g; + private final String h; + private final List i; + private final Map j; + private final List> k; + + private final DayData dayData; + + private TestData(final float a, final double b, final byte c, final short d, final int e, final long f, final boolean g, final String h, final List i, final Map j, final List> k, final DayData dayData) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + this.f = f; + this.g = g; + this.h = h; + this.i = i; + this.j = j; + this.k = k; + this.dayData = dayData; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final TestData testData = (TestData) o; + return Float.compare(testData.a, a) == 0 && + Double.compare(testData.b, b) == 0 && + c == testData.c && + d == testData.d && + e == testData.e && + f == testData.f && + g == testData.g && + h.equals(testData.h) && + i.equals(testData.i) && + j.equals(testData.j) && + k.equals(testData.k) && + dayData.equals(testData.dayData); + } + + @Override + public int hashCode() { + return Objects.hash(a, b, c, d, e, f, g, h, i, j, k, dayData); + } + } + + private static TestData makeRandomTestData() { + final Random random = new Random(4); + return new TestData( + random.nextFloat(), + random.nextDouble(), + (byte) random.nextInt(), + (short) random.nextInt(), + random.nextInt(), + random.nextLong(), + random.nextBoolean(), + Float.toString(random.nextFloat()), + IntStream.range(0, random.nextInt(100)) + .mapToObj(i -> Float.toString(random.nextFloat())) + .collect(Collectors.toList()), + IntStream.range(0, random.nextInt(100)) + .boxed() + .collect(Collectors.toMap( + i -> Float.toString(random.nextFloat()), + i -> Float.toString(random.nextFloat())) + ), + IntStream.range(0, random.nextInt(100)) + .mapToObj(i -> Pair.of(Float.toString(random.nextFloat()), Float.toString(random.nextFloat()))) + .collect(Collectors.toList() + ), + new WednesdayData("meetings lol")); + } + + private void testWriteRead(final DynamicOps ops) { + final TestData data = makeRandomTestData(); + + final DataResult encoded = TestData.CODEC.encodeStart(ops, data); + final DataResult decoded = encoded.flatMap(r -> TestData.CODEC.parse(ops, r)); + + assertEquals("read(write(x)) == x", DataResult.success(data), decoded); + } + + private void testReadWrite(final DynamicOps ops) { + final TestData data = makeRandomTestData(); + + final DataResult encoded = TestData.CODEC.encodeStart(ops, data); + final DataResult decoded = encoded.flatMap(r -> TestData.CODEC.parse(ops, r)); + final DataResult reEncoded = decoded.flatMap(r -> TestData.CODEC.encodeStart(ops, r)); + + assertEquals("write(read(x)) == x", encoded, reEncoded); + } + + @Test + public void testWriteReadNormal() { + testWriteRead(JsonOps.INSTANCE); + } + + @Test + public void testReadWriteNormal() { + testReadWrite(JsonOps.INSTANCE); + } + + @Test + public void testWriteReadCompressed() { + testWriteRead(JsonOps.COMPRESSED); + } + + @Test + public void testReadWriteCompressed() { + testReadWrite(JsonOps.COMPRESSED); + } +} \ No newline at end of file

TypedOptic, java.util.List, A, B> list(final Type aType, final Type bType) { + public static TypedOptic, List, A, B> list(final Type aType, final Type bType) { return new TypedOptic<>( TraversalP.Mu.TYPE_TOKEN, DSL.list(aType), diff --git a/src/main/java/com/mojang/datafixers/View.java b/src/main/java/com/mojang/datafixers/View.java index 53234c72..4139dc99 100644 --- a/src/main/java/com/mojang/datafixers/View.java +++ b/src/main/java/com/mojang/datafixers/View.java @@ -7,8 +7,8 @@ import com.mojang.datafixers.functions.PointFreeRule; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; +import com.mojang.serialization.DynamicOps; import java.util.Objects; import java.util.Optional; diff --git a/src/main/java/com/mojang/datafixers/functions/Apply.java b/src/main/java/com/mojang/datafixers/functions/Apply.java index 6325412b..32c55622 100644 --- a/src/main/java/com/mojang/datafixers/functions/Apply.java +++ b/src/main/java/com/mojang/datafixers/functions/Apply.java @@ -3,8 +3,8 @@ package com.mojang.datafixers.functions; import com.mojang.datafixers.DSL; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; +import com.mojang.serialization.DynamicOps; import java.util.Objects; import java.util.Optional; diff --git a/src/main/java/com/mojang/datafixers/functions/Bang.java b/src/main/java/com/mojang/datafixers/functions/Bang.java index a3d756a6..03db0265 100644 --- a/src/main/java/com/mojang/datafixers/functions/Bang.java +++ b/src/main/java/com/mojang/datafixers/functions/Bang.java @@ -2,7 +2,7 @@ // Licensed under the MIT license. package com.mojang.datafixers.functions; -import com.mojang.datafixers.types.DynamicOps; +import com.mojang.serialization.DynamicOps; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/functions/Comp.java b/src/main/java/com/mojang/datafixers/functions/Comp.java index 2d0245c2..a7a0ba66 100644 --- a/src/main/java/com/mojang/datafixers/functions/Comp.java +++ b/src/main/java/com/mojang/datafixers/functions/Comp.java @@ -3,9 +3,9 @@ package com.mojang.datafixers.functions; import com.mojang.datafixers.DSL; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Func; import com.mojang.datafixers.types.Type; +import com.mojang.serialization.DynamicOps; import java.util.Objects; import java.util.Optional; diff --git a/src/main/java/com/mojang/datafixers/functions/Fold.java b/src/main/java/com/mojang/datafixers/functions/Fold.java index c6cd021f..54f1afd4 100644 --- a/src/main/java/com/mojang/datafixers/functions/Fold.java +++ b/src/main/java/com/mojang/datafixers/functions/Fold.java @@ -5,11 +5,11 @@ import com.google.common.collect.Maps; import com.mojang.datafixers.RewriteResult; import com.mojang.datafixers.View; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.families.Algebra; import com.mojang.datafixers.types.families.RecursiveTypeFamily; import com.mojang.datafixers.types.templates.RecursivePoint; import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.DynamicOps; import java.util.Map; import java.util.Objects; diff --git a/src/main/java/com/mojang/datafixers/functions/FunctionWrapper.java b/src/main/java/com/mojang/datafixers/functions/FunctionWrapper.java index c58aed2a..07ec5b61 100644 --- a/src/main/java/com/mojang/datafixers/functions/FunctionWrapper.java +++ b/src/main/java/com/mojang/datafixers/functions/FunctionWrapper.java @@ -2,7 +2,7 @@ // Licensed under the MIT license. package com.mojang.datafixers.functions; -import com.mojang.datafixers.types.DynamicOps; +import com.mojang.serialization.DynamicOps; import java.util.Objects; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/functions/Functions.java b/src/main/java/com/mojang/datafixers/functions/Functions.java index f0b5562a..6a5e4023 100644 --- a/src/main/java/com/mojang/datafixers/functions/Functions.java +++ b/src/main/java/com/mojang/datafixers/functions/Functions.java @@ -5,10 +5,10 @@ import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.RewriteResult; import com.mojang.datafixers.optics.Optic; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; import com.mojang.datafixers.types.families.Algebra; import com.mojang.datafixers.types.templates.RecursivePoint; +import com.mojang.serialization.DynamicOps; import java.util.Objects; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/functions/Id.java b/src/main/java/com/mojang/datafixers/functions/Id.java index 7be4a901..141134a2 100644 --- a/src/main/java/com/mojang/datafixers/functions/Id.java +++ b/src/main/java/com/mojang/datafixers/functions/Id.java @@ -2,7 +2,7 @@ // Licensed under the MIT license. package com.mojang.datafixers.functions; -import com.mojang.datafixers.types.DynamicOps; +import com.mojang.serialization.DynamicOps; import java.util.function.Function; @@ -12,7 +12,7 @@ public Id() { @Override public boolean equals(final Object obj) { - return obj instanceof com.mojang.datafixers.functions.Id; + return obj instanceof Id; } @Override diff --git a/src/main/java/com/mojang/datafixers/functions/In.java b/src/main/java/com/mojang/datafixers/functions/In.java index 98249d89..8a530338 100644 --- a/src/main/java/com/mojang/datafixers/functions/In.java +++ b/src/main/java/com/mojang/datafixers/functions/In.java @@ -2,8 +2,8 @@ // Licensed under the MIT license. package com.mojang.datafixers.functions; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.templates.RecursivePoint; +import com.mojang.serialization.DynamicOps; import java.util.Objects; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/functions/Out.java b/src/main/java/com/mojang/datafixers/functions/Out.java index 8242d3b3..735a6d99 100644 --- a/src/main/java/com/mojang/datafixers/functions/Out.java +++ b/src/main/java/com/mojang/datafixers/functions/Out.java @@ -2,8 +2,8 @@ // Licensed under the MIT license. package com.mojang.datafixers.functions; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.templates.RecursivePoint; +import com.mojang.serialization.DynamicOps; import java.util.Objects; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/functions/PointFree.java b/src/main/java/com/mojang/datafixers/functions/PointFree.java index bcdbaad7..f7593e3b 100644 --- a/src/main/java/com/mojang/datafixers/functions/PointFree.java +++ b/src/main/java/com/mojang/datafixers/functions/PointFree.java @@ -2,8 +2,8 @@ // Licensed under the MIT license. package com.mojang.datafixers.functions; -import com.mojang.datafixers.types.DynamicOps; import com.mojang.datafixers.types.Type; +import com.mojang.serialization.DynamicOps; import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; diff --git a/src/main/java/com/mojang/datafixers/functions/PointFreeRule.java b/src/main/java/com/mojang/datafixers/functions/PointFreeRule.java index 68747734..eaee52d2 100644 --- a/src/main/java/com/mojang/datafixers/functions/PointFreeRule.java +++ b/src/main/java/com/mojang/datafixers/functions/PointFreeRule.java @@ -4,24 +4,22 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.DSL; import com.mojang.datafixers.DataFixUtils; -import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.RewriteResult; import com.mojang.datafixers.View; -import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K1; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.optics.Optic; import com.mojang.datafixers.optics.Optics; import com.mojang.datafixers.types.Func; import com.mojang.datafixers.types.Type; -import com.mojang.datafixers.types.constant.NilDrop; +import com.mojang.datafixers.types.constant.EmptyPart; import com.mojang.datafixers.types.families.Algebra; import com.mojang.datafixers.types.families.ListAlgebra; import com.mojang.datafixers.types.families.RecursiveTypeFamily; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import org.apache.commons.lang3.ObjectUtils; import java.util.BitSet; @@ -75,7 +73,7 @@ public Optional> rewrite(final Type type, final Po } if (type instanceof Func) { final Func func = (Func) type; - if (func.second() instanceof NilDrop) { + if (func.second() instanceof EmptyPart) { return Optional.of((PointFree) Functions.bang()); } } diff --git a/src/main/java/com/mojang/datafixers/functions/ProfunctorTransformer.java b/src/main/java/com/mojang/datafixers/functions/ProfunctorTransformer.java index 5e032e7e..a6240b48 100644 --- a/src/main/java/com/mojang/datafixers/functions/ProfunctorTransformer.java +++ b/src/main/java/com/mojang/datafixers/functions/ProfunctorTransformer.java @@ -5,7 +5,7 @@ import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.optics.Optic; -import com.mojang.datafixers.types.DynamicOps; +import com.mojang.serialization.DynamicOps; import java.util.Objects; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/kinds/Applicative.java b/src/main/java/com/mojang/datafixers/kinds/Applicative.java index e7f0c8c5..3ed14fa3 100644 --- a/src/main/java/com/mojang/datafixers/kinds/Applicative.java +++ b/src/main/java/com/mojang/datafixers/kinds/Applicative.java @@ -2,6 +2,21 @@ // Licensed under the MIT license. package com.mojang.datafixers.kinds; +import com.mojang.datafixers.util.Function10; +import com.mojang.datafixers.util.Function11; +import com.mojang.datafixers.util.Function12; +import com.mojang.datafixers.util.Function13; +import com.mojang.datafixers.util.Function14; +import com.mojang.datafixers.util.Function15; +import com.mojang.datafixers.util.Function16; +import com.mojang.datafixers.util.Function3; +import com.mojang.datafixers.util.Function4; +import com.mojang.datafixers.util.Function5; +import com.mojang.datafixers.util.Function6; +import com.mojang.datafixers.util.Function7; +import com.mojang.datafixers.util.Function8; +import com.mojang.datafixers.util.Function9; + import java.util.function.BiFunction; import java.util.function.Function; @@ -16,21 +31,136 @@ interface Mu extends Functor.Mu {} Function, App> lift1(final App> function); - BiFunction, App, App> lift2(final App> function); + default BiFunction, App, App> lift2(final App> function) { + return (fa, fb) -> ap2(function, fa, fb); + } + + default Function3, App, App, App> lift3(final App> function) { + return (ft1, ft2, ft3) -> ap3(function, ft1, ft2, ft3); + } + + default Function4, App, App, App, App> lift4(final App> function) { + return (ft1, ft2, ft3, ft4) -> ap4(function, ft1, ft2, ft3, ft4); + } + + default Function5, App, App, App, App, App> lift5(final App> function) { + return (ft1, ft2, ft3, ft4, ft5) -> ap5(function, ft1, ft2, ft3, ft4, ft5); + } + + default Function6, App, App, App, App, App, App> lift6(final App> function) { + return (ft1, ft2, ft3, ft4, ft5, ft6) -> ap6(function, ft1, ft2, ft3, ft4, ft5, ft6); + } + + default Function7, App, App, App, App, App, App, App> lift7(final App> function) { + return (ft1, ft2, ft3, ft4, ft5, ft6, ft7) -> ap7(function, ft1, ft2, ft3, ft4, ft5, ft6, ft7); + } + + default Function8, App, App, App, App, App, App, App, App> lift8(final App> function) { + return (ft1, ft2, ft3, ft4, ft5, ft6, ft7, ft8) -> ap8(function, ft1, ft2, ft3, ft4, ft5, ft6, ft7, ft8); + } + + default Function9, App, App, App, App, App, App, App, App, App> lift9(final App> function) { + return (ft1, ft2, ft3, ft4, ft5, ft6, ft7, ft8, ft9) -> ap9(function, ft1, ft2, ft3, ft4, ft5, ft6, ft7, ft8, ft9); + } default App ap(final App> func, final App arg) { return lift1(func).apply(arg); } default App ap(final Function func, final App arg) { - return lift1(point(func)).apply(arg); + return map(func, arg); } default App ap2(final App> func, final App a, final App b) { - return lift2(func).apply(a, b); + final Function, Function>> curry = f -> a1 -> b1 -> f.apply(a1, b1); + return ap(ap(map(curry, func), a), b); + } + + default App ap3(final App> func, final App t1, final App t2, final App t3) { + return ap2(ap(map(Function3::curry, func), t1), t2, t3); + } + + default App ap4(final App> func, final App t1, final App t2, final App t3, final App t4) { + return ap2(ap2(map(Function4::curry2, func), t1, t2), t3, t4); + } + + default App ap5(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5) { + return ap3(ap2(map(Function5::curry2, func), t1, t2), t3, t4, t5); + } + + default App ap6(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6) { + return ap3(ap3(map(Function6::curry3, func), t1, t2, t3), t4, t5, t6); + } + + default App ap7(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7) { + return ap4(ap3(map(Function7::curry3, func), t1, t2, t3), t4, t5, t6, t7); + } + + default App ap8(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8) { + return ap4(ap4(map(Function8::curry4, func), t1, t2, t3, t4), t5, t6, t7, t8); + } + + default App ap9(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9) { + return ap5(ap4(map(Function9::curry4, func), t1, t2, t3, t4), t5, t6, t7, t8, t9); + } + + default App ap10(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10) { + return ap5(ap5(map(Function10::curry5, func), t1, t2, t3, t4, t5), t6, t7, t8, t9, t10); + } + + default App ap11(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11) { + return ap6(ap5(map(Function11::curry5, func), t1, t2, t3, t4, t5), t6, t7, t8, t9, t10, t11); + } + + default App ap12(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12) { + return ap6(ap6(map(Function12::curry6, func), t1, t2, t3, t4, t5, t6), t7, t8, t9, t10, t11, t12); + } + + default App ap13(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13) { + return ap7(ap6(map(Function13::curry6, func), t1, t2, t3, t4, t5, t6), t7, t8, t9, t10, t11, t12, t13); + } + + default App ap14(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13, final App t14) { + return ap7(ap7(map(Function14::curry7, func), t1, t2, t3, t4, t5, t6, t7), t8, t9, t10, t11, t12, t13, t14); + } + + default App ap15(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13, final App t14, final App t15) { + return ap8(ap7(map(Function15::curry7, func), t1, t2, t3, t4, t5, t6, t7), t8, t9, t10, t11, t12, t13, t14, t15); + } + + default App ap16(final App> func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13, final App t14, final App t15, final App t16) { + return ap8(ap8(map(Function16::curry8, func), t1, t2, t3, t4, t5, t6, t7, t8), t9, t10, t11, t12, t13, t14, t15, t16); + } + + default App apply2(final BiFunction func, final App a, final App b) { + return ap2(point(func), a, b); + } + + default App apply3(final Function3 func, final App t1, final App t2, final App t3) { + return ap3(point(func), t1, t2, t3); + } + + default App apply4(final Function4 func, final App t1, final App t2, final App t3, final App t4) { + return ap4(point(func), t1, t2, t3, t4); + } + + default App apply5(final Function5 func, final App t1, final App t2, final App t3, final App t4, final App t5) { + return ap5(point(func), t1, t2, t3, t4, t5); + } + + default App apply6(final Function6 func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6) { + return ap6(point(func), t1, t2, t3, t4, t5, t6); + } + + default App apply7(final Function7 func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7) { + return ap7(point(func), t1, t2, t3, t4, t5, t6, t7); + } + + default App apply8(final Function8 func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8) { + return ap8(point(func), t1, t2, t3, t4, t5, t6, t7, t8); } - default App ap2(final BiFunction func, final App a, final App b) { - return lift2(point(func)).apply(a, b); + default App apply9(final Function9 func, final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9) { + return ap9(point(func), t1, t2, t3, t4, t5, t6, t7, t8, t9); } } diff --git a/src/main/java/com/mojang/datafixers/kinds/Kind1.java b/src/main/java/com/mojang/datafixers/kinds/Kind1.java index 1ecd910a..ec93205b 100644 --- a/src/main/java/com/mojang/datafixers/kinds/Kind1.java +++ b/src/main/java/com/mojang/datafixers/kinds/Kind1.java @@ -2,10 +2,76 @@ // Licensed under the MIT license. package com.mojang.datafixers.kinds; +import com.mojang.datafixers.Products; + public interface Kind1 extends App { static Kind1 unbox(final App proofBox) { return (Kind1) proofBox; } interface Mu extends K1 {} + + default Products.P1 group(final App t1) { + return new Products.P1<>(t1); + } + + default Products.P2 group(final App t1, final App t2) { + return new Products.P2<>(t1, t2); + } + + default Products.P3 group(final App t1, final App t2, final App t3) { + return new Products.P3<>(t1, t2, t3); + } + + default Products.P4 group(final App t1, final App t2, final App t3, final App t4) { + return new Products.P4<>(t1, t2, t3, t4); + } + + default Products.P5 group(final App t1, final App t2, final App t3, final App t4, final App t5) { + return new Products.P5<>(t1, t2, t3, t4, t5); + } + + default Products.P6 group(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6) { + return new Products.P6<>(t1, t2, t3, t4, t5, t6); + } + + default Products.P7 group(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7) { + return new Products.P7<>(t1, t2, t3, t4, t5, t6, t7); + } + + default Products.P8 group(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8) { + return new Products.P8<>(t1, t2, t3, t4, t5, t6, t7, t8); + } + + default Products.P9 group(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9) { + return new Products.P9<>(t1, t2, t3, t4, t5, t6, t7, t8, t9); + } + + default Products.P10 group(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10) { + return new Products.P10<>(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); + } + + default Products.P11 group(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11) { + return new Products.P11<>(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11); + } + + default Products.P12 group(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12) { + return new Products.P12<>(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12); + } + + default Products.P13 group(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13) { + return new Products.P13<>(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13); + } + + default Products.P14 group(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13, final App t14) { + return new Products.P14<>(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14); + } + + default Products.P15 group(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13, final App t14, final App t15) { + return new Products.P15<>(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15); + } + + default Products.P16 group(final App t1, final App t2, final App t3, final App t4, final App t5, final App t6, final App t7, final App t8, final App t9, final App t10, final App t11, final App t12, final App t13, final App t14, final App t15, final App t16) { + return new Products.P16<>(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16); + } } diff --git a/src/main/java/com/mojang/datafixers/kinds/ListBox.java b/src/main/java/com/mojang/datafixers/kinds/ListBox.java new file mode 100644 index 00000000..334cce7a --- /dev/null +++ b/src/main/java/com/mojang/datafixers/kinds/ListBox.java @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +package com.mojang.datafixers.kinds; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +public final class ListBox implements App { + public static final class Mu implements K1 {} + + public static List unbox(final App box) { + return ((ListBox) box).value; + } + + public static ListBox create(final List value) { + return new ListBox<>(value); + } + + private final List value; + + private ListBox(final List value) { + this.value = value; + } + + public static App> traverse(final Applicative applicative, final Function> function, final List input) { + return applicative.map(ListBox::unbox, Instance.INSTANCE.traverse(applicative, function, create(input))); + } + + public static App> flip(final Applicative applicative, final List> input) { + return applicative.map(ListBox::unbox, Instance.INSTANCE.flip(applicative, create(input))); + } + + public enum Instance implements Traversable { + INSTANCE; + + public static final class Mu implements Traversable.Mu {} + + @Override + public App map(final Function func, final App ts) { + return create(ListBox.unbox(ts).stream().map(func).collect(Collectors.toList())); + } + + @Override + public App> traverse(final Applicative applicative, final Function> function, final App input) { + final List list = unbox(input); + + App> result = applicative.point(ImmutableList.builder()); + + for (final A a : list) { + final App fb = function.apply(a); + result = applicative.ap2(applicative.point(ImmutableList.Builder::add), result, fb); + } + + return applicative.map(b -> create(b.build()), result); + } + } +} diff --git a/src/main/java/com/mojang/datafixers/kinds/OptionalBox.java b/src/main/java/com/mojang/datafixers/kinds/OptionalBox.java index 2c9623f5..01724a0f 100644 --- a/src/main/java/com/mojang/datafixers/kinds/OptionalBox.java +++ b/src/main/java/com/mojang/datafixers/kinds/OptionalBox.java @@ -19,14 +19,14 @@ public static OptionalBox create(final Optional value) { private final Optional value; - OptionalBox(final Optional value) { + private OptionalBox(final Optional value) { this.value = value; } - public enum Instance implements Applicative { + public enum Instance implements Applicative, Traversable { INSTANCE; - public static final class Mu implements Applicative.Mu {} + public static final class Mu implements Applicative.Mu, Traversable.Mu {} @Override public App map(final Function func, final App ts) { @@ -47,5 +47,14 @@ public Function, App> lift1(fin public BiFunction, App, App> lift2(final App> function) { return (a, b) -> create(OptionalBox.unbox(function).flatMap(f -> OptionalBox.unbox(a).flatMap(av -> OptionalBox.unbox(b).map(bv -> f.apply(av, bv))))); } + + @Override + public App> traverse(final Applicative applicative, final Function> function, final App input) { + final Optional> traversed = unbox(input).map(function); + if (traversed.isPresent()) { + return applicative.map(b -> OptionalBox.create(Optional.of(b)), traversed.get()); + } + return applicative.point(OptionalBox.create(Optional.empty())); + } } } diff --git a/src/main/java/com/mojang/datafixers/kinds/Traversable.java b/src/main/java/com/mojang/datafixers/kinds/Traversable.java index 7725859e..760d1175 100644 --- a/src/main/java/com/mojang/datafixers/kinds/Traversable.java +++ b/src/main/java/com/mojang/datafixers/kinds/Traversable.java @@ -13,7 +13,7 @@ interface Mu extends Functor.Mu {} App> traverse(final Applicative applicative, final Function> function, final App input); - default App> sequenceA(final Applicative applicative, final App> input) { + default App> flip(final Applicative applicative, final App> input) { return traverse(applicative, Function.identity(), input); } } diff --git a/src/main/java/com/mojang/datafixers/optics/Affine.java b/src/main/java/com/mojang/datafixers/optics/Affine.java index 284353be..9f7e4fee 100644 --- a/src/main/java/com/mojang/datafixers/optics/Affine.java +++ b/src/main/java/com/mojang/datafixers/optics/Affine.java @@ -2,8 +2,6 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; @@ -11,6 +9,8 @@ import com.mojang.datafixers.optics.profunctors.AffineP; import com.mojang.datafixers.optics.profunctors.Cartesian; import com.mojang.datafixers.optics.profunctors.Cocartesian; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/optics/Forget.java b/src/main/java/com/mojang/datafixers/optics/Forget.java index c60c479d..84a3735c 100644 --- a/src/main/java/com/mojang/datafixers/optics/Forget.java +++ b/src/main/java/com/mojang/datafixers/optics/Forget.java @@ -2,14 +2,14 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.optics.profunctors.Cartesian; import com.mojang.datafixers.optics.profunctors.ReCocartesian; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/optics/ForgetE.java b/src/main/java/com/mojang/datafixers/optics/ForgetE.java index 3ff22051..274d030a 100644 --- a/src/main/java/com/mojang/datafixers/optics/ForgetE.java +++ b/src/main/java/com/mojang/datafixers/optics/ForgetE.java @@ -2,13 +2,13 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.optics.profunctors.AffineP; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/optics/ForgetOpt.java b/src/main/java/com/mojang/datafixers/optics/ForgetOpt.java index 4b82829e..074d687e 100644 --- a/src/main/java/com/mojang/datafixers/optics/ForgetOpt.java +++ b/src/main/java/com/mojang/datafixers/optics/ForgetOpt.java @@ -2,13 +2,13 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.optics.profunctors.AffineP; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import java.util.Optional; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/optics/Lens.java b/src/main/java/com/mojang/datafixers/optics/Lens.java index 52c2e575..f8b068e6 100644 --- a/src/main/java/com/mojang/datafixers/optics/Lens.java +++ b/src/main/java/com/mojang/datafixers/optics/Lens.java @@ -2,12 +2,12 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.optics.profunctors.Cartesian; +import com.mojang.datafixers.util.Pair; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/optics/ListTraversal.java b/src/main/java/com/mojang/datafixers/optics/ListTraversal.java index b24d88ad..3f5d6880 100644 --- a/src/main/java/com/mojang/datafixers/optics/ListTraversal.java +++ b/src/main/java/com/mojang/datafixers/optics/ListTraversal.java @@ -16,7 +16,7 @@ public FunctionType, App>> wander(final Applic return as -> { App> result = applicative.point(ImmutableList.builder()); for (final A a : as) { - result = applicative.ap2(ImmutableList.Builder::add, result, input.apply(a)); + result = applicative.ap2(applicative.point(ImmutableList.Builder::add), result, input.apply(a)); } return applicative.map(ImmutableList.Builder::build, result); }; diff --git a/src/main/java/com/mojang/datafixers/optics/Optic.java b/src/main/java/com/mojang/datafixers/optics/Optic.java index e2574f91..077fb157 100644 --- a/src/main/java/com/mojang/datafixers/optics/Optic.java +++ b/src/main/java/com/mojang/datafixers/optics/Optic.java @@ -3,7 +3,6 @@ package com.mojang.datafixers.optics; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K1; diff --git a/src/main/java/com/mojang/datafixers/optics/Optics.java b/src/main/java/com/mojang/datafixers/optics/Optics.java index 76553b43..4bf469df 100644 --- a/src/main/java/com/mojang/datafixers/optics/Optics.java +++ b/src/main/java/com/mojang/datafixers/optics/Optics.java @@ -2,8 +2,6 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; @@ -15,6 +13,8 @@ import com.mojang.datafixers.optics.profunctors.GetterP; import com.mojang.datafixers.optics.profunctors.Profunctor; import com.mojang.datafixers.optics.profunctors.TraversalP; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import java.util.Optional; import java.util.function.BiFunction; @@ -23,32 +23,32 @@ public abstract class Optics { public static Adapter toAdapter(final Optic optic) { - final Function, A, B>, App2, S, T>> eval = optic.eval(new Adapter.Instance()); + final Function, A, B>, App2, S, T>> eval = optic.eval(new Adapter.Instance<>()); return Adapter.unbox(eval.apply(adapter(Function.identity(), Function.identity()))); } public static Lens toLens(final Optic optic) { - final Function, A, B>, App2, S, T>> eval = optic.eval(new Lens.Instance()); + final Function, A, B>, App2, S, T>> eval = optic.eval(new Lens.Instance<>()); return Lens.unbox(eval.apply(lens(Function.identity(), (b, a) -> b))); } public static Prism toPrism(final Optic optic) { - final Function, A, B>, App2, S, T>> eval = optic.eval(new Prism.Instance()); + final Function, A, B>, App2, S, T>> eval = optic.eval(new Prism.Instance<>()); return Prism.unbox(eval.apply(prism(Either::right, Function.identity()))); } public static Affine toAffine(final Optic optic) { - final Function, A, B>, App2, S, T>> eval = optic.eval(new Affine.Instance()); + final Function, A, B>, App2, S, T>> eval = optic.eval(new Affine.Instance<>()); return Affine.unbox(eval.apply(affine(Either::right, (b, a) -> b))); } public static Getter toGetter(final Optic optic) { - final Function, A, B>, App2, S, T>> eval = optic.eval(new Getter.Instance()); + final Function, A, B>, App2, S, T>> eval = optic.eval(new Getter.Instance<>()); return Getter.unbox(eval.apply(getter(Function.identity()))); } public static Traversal toTraversal(final Optic optic) { - final Function, A, B>, App2, S, T>> eval = optic.eval(new Traversal.Instance()); + final Function, A, B>, App2, S, T>> eval = optic.eval(new Traversal.Instance<>()); return Traversal.unbox(eval.apply(new Traversal() { @Override public FunctionType> wander(final Applicative applicative, final FunctionType> input) { diff --git a/src/main/java/com/mojang/datafixers/optics/Prism.java b/src/main/java/com/mojang/datafixers/optics/Prism.java index dead8ed0..330842ad 100644 --- a/src/main/java/com/mojang/datafixers/optics/Prism.java +++ b/src/main/java/com/mojang/datafixers/optics/Prism.java @@ -2,12 +2,12 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Either; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.optics.profunctors.Cocartesian; +import com.mojang.datafixers.util.Either; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/optics/ReForget.java b/src/main/java/com/mojang/datafixers/optics/ReForget.java index 72329eda..ab5f4da2 100644 --- a/src/main/java/com/mojang/datafixers/optics/ReForget.java +++ b/src/main/java/com/mojang/datafixers/optics/ReForget.java @@ -2,14 +2,14 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.optics.profunctors.Cocartesian; import com.mojang.datafixers.optics.profunctors.ReCartesian; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/optics/ReForgetC.java b/src/main/java/com/mojang/datafixers/optics/ReForgetC.java index 47e984e0..5a8858da 100644 --- a/src/main/java/com/mojang/datafixers/optics/ReForgetC.java +++ b/src/main/java/com/mojang/datafixers/optics/ReForgetC.java @@ -2,13 +2,13 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.optics.profunctors.AffineP; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import java.util.function.BiFunction; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/optics/ReForgetE.java b/src/main/java/com/mojang/datafixers/optics/ReForgetE.java index c5a43287..aec5f989 100644 --- a/src/main/java/com/mojang/datafixers/optics/ReForgetE.java +++ b/src/main/java/com/mojang/datafixers/optics/ReForgetE.java @@ -2,12 +2,12 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Either; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.optics.profunctors.Cocartesian; +import com.mojang.datafixers.util.Either; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/optics/ReForgetEP.java b/src/main/java/com/mojang/datafixers/optics/ReForgetEP.java index 04db8915..01b2c82f 100644 --- a/src/main/java/com/mojang/datafixers/optics/ReForgetEP.java +++ b/src/main/java/com/mojang/datafixers/optics/ReForgetEP.java @@ -2,13 +2,13 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.optics.profunctors.AffineP; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/optics/ReForgetP.java b/src/main/java/com/mojang/datafixers/optics/ReForgetP.java index a47410e4..367631a5 100644 --- a/src/main/java/com/mojang/datafixers/optics/ReForgetP.java +++ b/src/main/java/com/mojang/datafixers/optics/ReForgetP.java @@ -2,13 +2,13 @@ // Licensed under the MIT license. package com.mojang.datafixers.optics; -import com.mojang.datafixers.util.Either; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.FunctionType; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.K2; import com.mojang.datafixers.optics.profunctors.AffineP; +import com.mojang.datafixers.util.Either; +import com.mojang.datafixers.util.Pair; import java.util.function.Function; diff --git a/src/main/java/com/mojang/datafixers/optics/profunctors/Cartesian.java b/src/main/java/com/mojang/datafixers/optics/profunctors/Cartesian.java index e6fd1dc4..37404b77 100644 --- a/src/main/java/com/mojang/datafixers/optics/profunctors/Cartesian.java +++ b/src/main/java/com/mojang/datafixers/optics/profunctors/Cartesian.java @@ -3,12 +3,12 @@ package com.mojang.datafixers.optics.profunctors; import com.google.common.reflect.TypeToken; -import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.App2; import com.mojang.datafixers.kinds.CartesianLike; import com.mojang.datafixers.kinds.K1; import com.mojang.datafixers.kinds.K2; +import com.mojang.datafixers.util.Pair; public interface Cartesian