Skip to content

Commit abd98d6

Browse files
committed
last minute json changes
1 parent c403e23 commit abd98d6

File tree

14 files changed

+679
-171
lines changed

14 files changed

+679
-171
lines changed

binary/src/main/java/alpine/binary/ArrayBinaryCodec.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,26 @@
22

33
import io.netty.buffer.ByteBuf;
44

5+
import java.util.function.IntFunction;
6+
57
/**
68
* A binary codec which serializes a sequence of values as an array.
79
* @param parent The binary codec to serialize the values with.
10+
* @param constructor A function to allocate the correctly-typed array given a length.
811
* @param <T> The element type.
912
* @author mudkip
1013
*/
11-
record ArrayBinaryCodec<T>(BinaryCodec<T> parent) implements BinaryCodec<T[]> {
12-
@SuppressWarnings("unchecked")
14+
record ArrayBinaryCodec<T>(BinaryCodec<T> parent, IntFunction<T[]> constructor) implements BinaryCodec<T[]> {
1315
@Override
1416
public T[] read(ByteBuf buffer) {
1517
var length = VARINT.read(buffer);
16-
var data = new Object[length];
18+
var data = this.constructor.apply(length);
1719

1820
for (var index = 0; index < length; index++) {
1921
data[index] = this.parent.read(buffer);
2022
}
2123

22-
return (T[]) data;
24+
return data;
2325
}
2426

2527
@Override

binary/src/main/java/alpine/binary/BinaryCodec.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.Optional;
1010
import java.util.function.BiConsumer;
1111
import java.util.function.Function;
12+
import java.util.function.IntFunction;
1213
import java.util.function.Supplier;
1314

1415
/**
@@ -58,8 +59,8 @@ default BinaryCodec<List<T>> list() {
5859
return new ListBinaryCodec<>(this);
5960
}
6061

61-
default BinaryCodec<T[]> array() {
62-
return new ArrayBinaryCodec<>(this);
62+
default BinaryCodec<T[]> array(IntFunction<T[]> constructor) {
63+
return new ArrayBinaryCodec<>(this, constructor);
6364
}
6465

6566
/**
Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
package alpine.binary;
22

3-
import io.netty.buffer.ByteBuf;
4-
5-
import javax.imageio.ImageIO;
6-
import java.awt.image.BufferedImage;
7-
import java.io.ByteArrayInputStream;
8-
import java.io.ByteArrayOutputStream;
9-
import java.io.IOException;
103
import java.nio.charset.StandardCharsets;
114
import java.util.UUID;
125

13-
import static alpine.binary.ArrayBinaryCodecs.BYTE_ARRAY;
146
import static alpine.binary.BinaryCodec.LONG;
157

168
/**
@@ -28,29 +20,4 @@ interface StandardBinaryCodecs {
2820
LONG, java.util.UUID::getMostSignificantBits,
2921
LONG, java.util.UUID::getLeastSignificantBits,
3022
UUID::new);
31-
32-
/**
33-
* Returns binary codec which serializes an image.
34-
* @return A binary codec which serializes an image.
35-
* @see java.awt.image.BufferedImage
36-
*/
37-
static BinaryCodec<BufferedImage> image(String format) {
38-
return BYTE_ARRAY.map(
39-
array -> {
40-
try (var stream = new ByteArrayInputStream(array)) {
41-
return ImageIO.read(stream);
42-
} catch (IOException e) {
43-
throw new RuntimeException("Failed to read image!", e);
44-
}
45-
},
46-
image -> {
47-
try {
48-
var stream = new ByteArrayOutputStream();
49-
ImageIO.write(image, format, stream);
50-
return stream.toByteArray();
51-
} catch (IOException e) {
52-
throw new RuntimeException("Failed to write image!", e);
53-
}
54-
});
55-
}
5623
}

json/src/main/java/alpine/json/codec/Codec.java

Lines changed: 138 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,147 @@
11
package alpine.json.codec;
22

3-
public interface Codec<T> extends PrimitiveCodecs {
3+
import org.jetbrains.annotations.Nullable;
4+
5+
import java.util.ArrayList;
6+
import java.util.LinkedHashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.Optional;
10+
import java.util.function.Function;
11+
import java.util.function.IntFunction;
12+
13+
public interface Codec<T> extends PrimitiveCodecs, StandardCodecs, PrimitiveArrayCodecs {
414
<R> T decode(Transcoder<R> transcoder, R value);
515

616
<R> R encode(Transcoder<R> transcoder, T value);
717

18+
default <U> Codec<U> map(Function<T, U> to, Function<U, T> from) {
19+
return new Codec<>() {
20+
@Override
21+
public <R> U decode(Transcoder<R> transcoder, R value) {
22+
return to.apply(Codec.this.decode(transcoder, value));
23+
}
24+
25+
@Override
26+
public <R> R encode(Transcoder<R> transcoder, U value) {
27+
return Codec.this.encode(transcoder, from.apply(value));
28+
}
29+
};
30+
}
31+
32+
default Codec<@Nullable T> nullable() {
33+
return new Codec<>() {
34+
@Override
35+
public <R> @Nullable T decode(Transcoder<R> transcoder, R value) {
36+
return transcoder.isNull(value) ? null : Codec.this.decode(transcoder, value);
37+
}
38+
39+
@Override
40+
public <R> R encode(Transcoder<R> transcoder, @Nullable T value) {
41+
return value == null ? transcoder.encodeNull() : Codec.this.encode(transcoder, value);
42+
}
43+
};
44+
}
45+
46+
default Codec<Optional<T>> optional() {
47+
return new Codec<>() {
48+
@Override
49+
public <R> Optional<T> decode(Transcoder<R> transcoder, R value) {
50+
return transcoder.isNull(value) ? Optional.empty() : Optional.of(Codec.this.decode(transcoder, value));
51+
}
52+
53+
@Override
54+
public <R> R encode(Transcoder<R> transcoder, Optional<T> value) {
55+
return value.isEmpty() ? transcoder.encodeNull() : Codec.this.encode(transcoder, value.get());
56+
}
57+
};
58+
}
59+
60+
default Codec<List<T>> list() {
61+
return new Codec<>() {
62+
@Override
63+
public <R> List<T> decode(Transcoder<R> transcoder, R value) {
64+
return transcoder.decodeArray(value).stream()
65+
.map(element -> Codec.this.decode(transcoder, element))
66+
.toList();
67+
}
68+
69+
@Override
70+
public <R> R encode(Transcoder<R> transcoder, List<T> value) {
71+
return transcoder.encodeArray(value.stream()
72+
.map(element -> Codec.this.encode(transcoder, element))
73+
.toList());
74+
}
75+
};
76+
}
77+
78+
default Codec<T[]> array(IntFunction<T[]> constructor) {
79+
return new Codec<>() {
80+
@Override
81+
public <R> T[] decode(Transcoder<R> transcoder, R value) {
82+
var elements = transcoder.decodeArray(value);
83+
var result = constructor.apply(elements.size());
84+
85+
for (var index = 0; index < elements.size(); index++) {
86+
result[index] = Codec.this.decode(transcoder, elements.get(index));
87+
}
88+
89+
return result;
90+
}
91+
92+
@Override
93+
public <R> R encode(Transcoder<R> transcoder, T[] array) {
94+
var elements = new ArrayList<R>(array.length);
95+
96+
for (var item : array) {
97+
elements.add(Codec.this.encode(transcoder, item));
98+
}
99+
100+
return transcoder.encodeArray(elements);
101+
}
102+
};
103+
}
104+
105+
static <V> Codec<Map<String, V>> map(Codec<V> valueCodec) {
106+
return new Codec<>() {
107+
@Override
108+
public <R> Map<String, V> decode(Transcoder<R> transcoder, R value) {
109+
var decoded = new LinkedHashMap<String, V>();
110+
transcoder.decodeObject(value).forEach((key, entry) -> decoded.put(key, valueCodec.decode(transcoder, entry)));
111+
return decoded;
112+
}
113+
114+
@Override
115+
public <R> R encode(Transcoder<R> transcoder, Map<String, V> value) {
116+
var encoded = new LinkedHashMap<String, R>();
117+
value.forEach((key, entry) -> encoded.put(key, valueCodec.encode(transcoder, entry)));
118+
return transcoder.encodeObject(encoded);
119+
}
120+
};
121+
}
122+
123+
static <E extends Enum<E>> Codec<E> name(Class<E> enumClass) {
124+
return new Codec<>() {
125+
@Override
126+
public <R> E decode(Transcoder<R> transcoder, R value) {
127+
var name = transcoder.decodeString(value);
128+
129+
for (var constant : enumClass.getEnumConstants()) {
130+
if (constant.name().toLowerCase().equals(name)) {
131+
return constant;
132+
}
133+
}
134+
135+
throw new DecodingException("Unknown enum constant: " + name);
136+
}
137+
138+
@Override
139+
public <R> R encode(Transcoder<R> transcoder, E value) {
140+
return transcoder.encodeString(value.name().toLowerCase());
141+
}
142+
};
143+
}
144+
8145
static <T> CodecBuilder._0<T> builder() {
9146
return new CodecBuilder._0<>();
10147
}

json/src/main/java/alpine/json/codec/JsonTranscoder.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
package alpine.json.codec;
22

3+
import alpine.json.ArrayElement;
34
import alpine.json.BooleanElement;
45
import alpine.json.Element;
6+
import alpine.json.NullElement;
57
import alpine.json.NumberElement;
68
import alpine.json.ObjectElement;
79
import alpine.json.StringElement;
810

11+
import java.util.LinkedHashMap;
12+
import java.util.List;
913
import java.util.Map;
1014

1115
import static alpine.json.Element.*;
1216

1317
final class JsonTranscoder implements Transcoder<Element> {
18+
@Override
19+
public boolean isNull(Element value) {
20+
return value instanceof NullElement;
21+
}
22+
1423
@Override
1524
public Element encodeNull() {
1625
return nil();
@@ -58,6 +67,31 @@ public Element encodeString(String value) {
5867
return string(value);
5968
}
6069

70+
@Override
71+
public List<Element> decodeArray(Element value) {
72+
if (value instanceof ArrayElement element) {
73+
return element.stream().toList();
74+
} else {
75+
throw new DecodingException("Expected an array!");
76+
}
77+
}
78+
79+
@Override
80+
public Element encodeArray(List<Element> elements) {
81+
return array(elements);
82+
}
83+
84+
@Override
85+
public Map<String, Element> decodeObject(Element value) {
86+
if (value instanceof ObjectElement element) {
87+
var map = new LinkedHashMap<String, Element>();
88+
element.each(map::put);
89+
return map;
90+
} else {
91+
throw new DecodingException("Expected an object!");
92+
}
93+
}
94+
6195
@Override
6296
public Element decodeObjectField(Element object, String key) {
6397
if (object instanceof ObjectElement element) {

0 commit comments

Comments
 (0)