Skip to content

Instantly share code, notes, and snippets.

@raydac
Last active June 25, 2016 13:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raydac/28d770307bd33683aa17ea3c39d5e2c4 to your computer and use it in GitHub Desktop.
Save raydac/28d770307bd33683aa17ea3c39d5e2c4 to your computer and use it in GitHub Desktop.
Function to generate [JBBP DSL script](https://github.com/raydac/java-binary-block-parser) from a class contains fields marked by `@Bin` annotations
/**
* Generates <a href="https://github.com/raydac/java-binary-block-parser">JBBP DSL script</a> from class @BIN annotated fields. Extra field of @BIN annotation is used as expression for array length if the text starts with 'expr:'
* <p>Example code:
* <pre>{@code
* public static class Chunk {
* @Bin(outOrder = 1, type = BinType.INT)
* int length;
* @Bin(outOrder = 2, type = BinType.INT)
* int type;
* @Bin(outOrder = 3, type = BinType.BYTE_ARRAY, extra = "expr:length")
* byte[] data;
* @Bin(outOrder = 4, type = BinType.INT)
* int crc;
* }
*
* public static class PNGFormat {
* @Bin(outOrder = 1, type = BinType.LONG)
* long header;
* @Bin(outOrder = 2, type = BinType.STRUCT_ARRAY)
* Chunk[] chunks;
* }
*
* public static void main(String... args) throws Exception {
* System.out.println(class2jbbp(PNGFormat.class));
* }
* }</pre>
* </p>
*
* @param klazz source class for generating, must not be null
* @return generated JBBP DSL script for the class and its fields, it can be empty but can't be null
*
* @author Igor Maznitsa
*/
public static String class2jbbp(final Class<?> klazz) {
class Item implements Comparable<Item> {
final String fieldName;
final Class<?> fieldType;
final JBBPBitOrder bitOrder;
final JBBPBitNumber bitNumber;
final boolean array;
final BinType type;
final int order;
final String extra;
Item(final int order, final Bin binAnnotation, final Field fld) {
this.array = fld.getType().isArray();
if (this.array) {
this.fieldType = fld.getType().getComponentType();
} else {
this.fieldType = fld.getType();
}
this.fieldName = binAnnotation.name().isEmpty() ? fld.getName() : binAnnotation.name();
this.bitOrder = binAnnotation.bitOrder();
this.bitNumber = binAnnotation.outBitNumber();
this.type = binAnnotation.type();
this.order = binAnnotation.outOrder() < 0 ? order : binAnnotation.outOrder();
String expr = binAnnotation.extra();
expr = expr.startsWith("expr:") ? expr.substring(5) : null;
this.extra = expr;
}
@Override
public int compareTo(final Item o) {
if (this.order < o.order) {
return -1;
}
if (this.order > o.order) {
return 1;
}
return 0;
}
@Override
public String toString() {
final StringBuilder result = new StringBuilder();
switch (this.type) {
case UNDEFINED:
throw new IllegalArgumentException("Detected undefined field : " + this.fieldName);
case STRUCT:
case STRUCT_ARRAY: {
result.append(this.fieldName);
if (this.array) {
result.append(" [").append(this.extra == null || this.extra.isEmpty() ? "_" : this.extra).append("] ");
}
result.append(" {").append(class2jbbp(this.fieldType)).append("}");
}
break;
default: {
final String[] splitted = this.type.name().toLowerCase(Locale.ENGLISH).split("\\_");
result.append(splitted[0]);
if (splitted.length > 1) {
result.append(" [").append(this.extra == null || this.extra.isEmpty() ? "_" : this.extra).append("]");
}
result.append(' ').append(this.fieldName).append("; ");
}
break;
}
return result.toString();
}
}
final List<Item> items = new ArrayList<>();
int index = 0;
for (final Field f : klazz.getDeclaredFields()) {
final Bin anno = f.getAnnotation(Bin.class);
if (anno != null) {
items.add(new Item(index++, anno, f));
}
}
Collections.sort(items);
final StringBuilder result = new StringBuilder();
for (final Item i : items) {
result.append(i.toString());
}
return result.toString();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment