Skip to content

Instantly share code, notes, and snippets.

@richardstephens
Created December 5, 2024 15:29
Show Gist options
  • Save richardstephens/7b90d0d8c161dfca9e44851d1d1490c6 to your computer and use it in GitHub Desktop.
Save richardstephens/7b90d0d8c161dfca9e44851d1d1490c6 to your computer and use it in GitHub Desktop.
CapnpDebugDecode
import org.capnproto.StructList;
import org.capnproto.StructReader;
import org.capnproto.Text;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
/// Decodes a Cap'n'proto StructList.Reader or Struct.Reader to a Java List/Map.
/// Enables quickly getting the shape of capnp-encoded data without having to
/// shuffle it into external tools.
/// Note: not complete, only most common cases are supported.
public class CapnpDebugDecode {
public static ArrayList<Object> decodeStructList(StructList.Reader reader) {
try {
ArrayList<Object> list = new ArrayList<>();
for (int ii = 0; ii < reader.size(); ii++) {
Object o = reader.get(ii);
if (StructReader.class.isInstance(o)) {
list.add(decodeStruct((StructReader) o));
} else {
throw new IllegalArgumentException("Unknown type in list: " + o.getClass());
}
}
return list;
} catch (IllegalArgumentException e) {
throw new CapnpDebugDecodeException(e);
}
}
public static Map<String, Object> decodeStruct(org.capnproto.StructReader reader) {
try {
Map<String, Object> result = new HashMap<>();
var rClass = reader.getClass();
HashSet<String> isMethods = new HashSet<>();
for (var m : reader.getClass().getMethods()) {
if (m.getDeclaringClass() == rClass) {
if (m.getName().startsWith("is") && m.getReturnType() == boolean.class) {
var baseName = m.getName().substring(2);
isMethods.add(baseName);
if ((boolean) m.invoke(reader)) {
var getMethod = reader.getClass().getMethod("get" + baseName);
if (org.capnproto.StructReader.class.isAssignableFrom(
getMethod.getReturnType())) {
result.put(
baseName,
decodeStruct((StructReader) getMethod.invoke(reader)));
}
}
}
}
}
for (var m : reader.getClass().getMethods()) {
if (m.getDeclaringClass() == rClass) {
if (m.getName().startsWith("get")) {
String baseName = m.getName().substring(3);
if (!isMethods.contains(baseName)) {
if (org.capnproto.StructReader.class.isAssignableFrom(
m.getReturnType())) {
result.put(baseName, decodeStruct((StructReader) m.invoke(reader)));
} else if (StructList.Reader.class.isAssignableFrom(
m.getReturnType())) {
result.put(
baseName,
decodeStructList((StructList.Reader) m.invoke(reader)));
} else if (boolean.class.isAssignableFrom(m.getReturnType())
|| long.class.isAssignableFrom(m.getReturnType())
|| int.class.isAssignableFrom(m.getReturnType())) {
result.put(baseName, m.invoke(reader));
} else if (Text.Reader.class.isAssignableFrom(m.getReturnType())
|| m.getReturnType().isEnum()) {
result.put(baseName, m.invoke(reader).toString());
} else {
throw new UnsupportedOperationException(
"Method: "
+ m.getName()
+ " with unknown return type: "
+ m.getReturnType());
}
}
}
}
}
return result;
} catch (InvocationTargetException
| IllegalAccessException
| NoSuchMethodException
| UnsupportedOperationException e) {
throw new CapnpDebugDecodeException(e);
}
}
public static class CapnpDebugDecodeException extends RuntimeException {
public CapnpDebugDecodeException(Exception e) {
super(e);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment