Skip to content

Commit

Permalink
Update IonCodec to support map based values.
Browse files Browse the repository at this point in the history
  • Loading branch information
neilcsmith-net committed Oct 13, 2023
1 parent 82d1756 commit 2e73a40
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ class IonCodec {
private static final String ERROR = "Error";
private static final String SYSTEM = "System";

private static final String TYPE_MAP = Value.Type.of(PMap.class).name();
private static final String TYPE_ARRAY = Value.Type.of(PArray.class).name();
private static final String TYPE_MAP = PMap.TYPE_NAME;
private static final String TYPE_ARRAY = PArray.TYPE_NAME;

private static final String FIELD_MATCH_ID = "matchID";
private static final String FIELD_TO = "to";
Expand Down Expand Up @@ -126,11 +126,16 @@ private Message readMessage(IonReader reader) throws IOException {
}
try {
return switch (annotations[0]) {
case SEND -> readSendMessage(reader);
case SERVICE -> readServiceMessage(reader);
case REPLY -> readReplyMessage(reader);
case ERROR -> readErrorMessage(reader);
case SYSTEM -> readSystemMessage(reader);
case SEND ->
readSendMessage(reader);
case SERVICE ->
readServiceMessage(reader);
case REPLY ->
readReplyMessage(reader);
case ERROR ->
readErrorMessage(reader);
case SYSTEM ->
readSystemMessage(reader);
default ->
throw new IOException("Unknown message type");
};
Expand Down Expand Up @@ -301,9 +306,9 @@ private Value readValue(IonReader reader) throws Exception {
case INT ->
PNumber.of(reader.intValue());
case LIST -> {
var annotations = reader.getTypeAnnotations();
if (annotations.length > 0 && TYPE_MAP.equals(annotations[0])) {
yield readMap(reader);
String[] annotations = reader.getTypeAnnotations();
if (isMap(annotations)) {
yield readMapValue(annotations, reader);
} else {
yield PArray.of(readValues(reader));
}
Expand All @@ -313,6 +318,38 @@ private Value readValue(IonReader reader) throws Exception {
};
}

private boolean isMap(String[] annotations) {
for (String annotation : annotations) {
if (PMap.TYPE_NAME.equals(annotation)) {
return true;
}
}
return false;
}

private Value readMapValue(String[] annotations, IonReader reader) throws Exception {
Value.Type<?> type = null;
if (annotations.length > 1) {
for (String annotation : annotations) {
if (PMap.TYPE_NAME.equals(annotation)) {
continue;
}
var vt = Value.Type.fromName(annotation).orElse(null);
if (vt != null && PMap.MapBasedValue.class.isAssignableFrom(vt.asClass())) {
type = vt;
break;
}
}
}
PMap map = readMap(reader);
if (type != null) {
Value v = type.converter().apply(map).orElse(null);
return v == null ? map : v;
} else {
return map;
}
}

private PMap readMap(IonReader reader) throws Exception {
if (reader.getType() != IonType.LIST) {
throw new IllegalArgumentException("Not a list");
Expand Down Expand Up @@ -340,14 +377,16 @@ private void writeValues(IonWriter writer, List<Value> values) throws IOExceptio
private void writeValue(IonWriter writer, Value value) throws IOException {
if (value instanceof PNumber n) {
writeNumber(writer, n);
} else if (value instanceof PMap m) {
writeMap(writer, m);
} else if (value instanceof PArray a) {
writeArray(writer, a);
} else if (value instanceof PBytes b) {
writeBytes(writer, b);
} else if (value instanceof PBoolean b) {
writer.writeBool(b.value());
} else if (value instanceof PMap m) {
writeMap(writer, m, TYPE_MAP);
} else if (value instanceof PMap.MapBasedValue v) {
writeMap(writer, v.dataMap(), v.type().name(), TYPE_MAP);
} else {
writer.writeString(value.toString());
}
Expand All @@ -361,8 +400,8 @@ private void writeNumber(IonWriter writer, PNumber number) throws IOException {
}
}

private void writeMap(IonWriter writer, PMap map) throws IOException {
writer.setTypeAnnotations(TYPE_MAP);
private void writeMap(IonWriter writer, PMap map, String ... annotations) throws IOException {
writer.setTypeAnnotations(annotations);
writer.stepIn(IonType.LIST);
for (var key : map.keys()) {
writer.writeString(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,16 @@
import com.amazon.ion.system.IonTextWriterBuilder;
import java.net.URI;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.praxislive.core.ComponentInfo;
import org.praxislive.core.ControlAddress;
import org.praxislive.core.ControlPort;
import org.praxislive.core.Info;
import org.praxislive.core.Value;
import org.praxislive.core.protocols.ComponentProtocol;
import org.praxislive.core.types.PArray;
import org.praxislive.core.types.PBoolean;
import org.praxislive.core.types.PBytes;
Expand All @@ -45,12 +52,27 @@
*/
public class IonCodecTest {

private static final boolean DEBUG_INFO = false;
private static final boolean VERBOSE = Boolean.getBoolean("praxis.test.verbose");

private static final PBytes bytes = PBytes.valueOf(new byte[]{
(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE
});

@BeforeEach
public void beforeEach(TestInfo info) {
if (VERBOSE) {
System.out.println("START TEST : " + info.getDisplayName());
}
}

@AfterEach
public void afterEach(TestInfo info) {
if (VERBOSE) {
System.out.println("END TEST : " + info.getDisplayName());
System.out.println("=====================================");
}
}

@Test
public void testSendMessage() throws Exception {
var matchID = 1234;
Expand Down Expand Up @@ -157,12 +179,38 @@ public void testMultiMessage() throws Exception {
assertEquals(msg3, msgList.get(2));
assertEquals(msg4, msgList.get(3));
assertEquals(msg5, msgList.get(4));


}

@Test
public void testMapBasedValues() throws Exception {
int matchID = -987654321;
var customProp = PMap.of("flag", true, "data", bytes);
var info = Info.component()
.merge(ComponentProtocol.API_INFO)
.port("in", Info.port().input(ControlPort.class).build())
.port("out", Info.port().output(ControlPort.class).build())
.property("custom", customProp)
.build();
var msg = new Message.Reply(matchID, List.of(info));
var msgList = roundTrip(List.of(msg));
assertEquals(1, msgList.size());
var decoded = (Message.Reply) msgList.get(0);
assertEquals(matchID, decoded.matchID());
var decodedInfo = decoded.args().get(0);
assertTrue(info.equivalent(decodedInfo));
assertTrue(decodedInfo instanceof ComponentInfo);
assertEquals(info, decodedInfo);
var byteProp = ComponentInfo.from(decodedInfo)
.flatMap(i -> PMap.from(i.properties().get("custom")))
.map(m -> m.get("data"))
.orElseThrow();
assertEquals(bytes, byteProp);
}

private List<Message> roundTrip(List<Message> messages) throws Exception {
byte[] data = IonCodec.getDefault().writeMessages(messages);
if (DEBUG_INFO) {
if (VERBOSE) {
var sb = new StringBuilder();
sb.append("\nMESSAGE\n=======\n");
var system = IonSystemBuilder.standard().build();
Expand Down

0 comments on commit 2e73a40

Please sign in to comment.