I'm trying to use Preon to parse binary files, which are structured as a sequence of variable length records. For each record, there's a number which specifies the record length (in bytes).
Here's a simplified version of what I'm trying to do:
package test.preon;
import nl.flotsam.preon.annotation.BoundList;
import nl.flotsam.preon.annotation.BoundNumber;
import java.util.List;
public class BinFile {
@BoundNumber(size="16") int numberOfRecords;
@BoundList(type=Record.class, size="numberOfRecords") List<Record> records;
public int getNumberOfRecords() {
return numberOfRecords;
}
public List<Record> getRecords() {
return records;
}
public class Record {
@BoundNumber(size="16") int recordLength;
@BoundList(size="recordLength") byte[] data;
public int getRecordLength() {
return recordLength;
}
public byte[] getData() {
return data;
}
}
}
So, numberOfRecords specifies the number of records in the file and recordLength specifies the length of each record. The problem is that Preon isn't able to resolve recordLength in Record, although numberOfRecords works fine in BinFile.
Here's the exception I get:
nl.flotsam.limbo.BindingException: Failed to resolve recordLength on class test.preon.BinFile
at nl.flotsam.preon.codec.BindingsContext$BindingsResolver.get(BindingsContext.java:412)
at nl.flotsam.preon.codec.BindingsContext$BindingReference.resolve(BindingsContext.java:247)
at nl.flotsam.preon.codec.BindingsContext$BindingReference.resolve(BindingsContext.java:189)
at nl.flotsam.limbo.ast.ReferenceNode.eval(ReferenceNode.java:57)
at nl.flotsam.limbo.ast.ArithmeticNode$Operator$5.eval(ArithmeticNode.java:109)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:250)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:33)
at nl.flotsam.limbo.ast.ArithmeticNode$Operator$3.eval(ArithmeticNode.java:83)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:250)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:33)
at nl.flotsam.limbo.ast.ArithmeticNode$Operator$5.eval(ArithmeticNode.java:109)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:250)
at nl.flotsam.limbo.ast.ArithmeticNode.eval(ArithmeticNode.java:33)
at nl.flotsam.preon.codec.ListCodecFactory$SwitchingListCodec.decode(ListCodecFactory.java:458)
at nl.flotsam.preon.codec.ListCodecFactory$SwitchingListCodec.decode(ListCodecFactory.java:443)
at nl.flotsam.preon.binding.StandardBindingFactory$FieldBinding.load(StandardBindingFactory.java:128)
at nl.flotsam.preon.codec.ObjectCodecFactory$ObjectCodec.decode(ObjectCodecFactory.java:251)
at nl.flotsam.preon.DefaultCodecFactory$DefaultCodec.decode(DefaultCodecFactory.java:173)
at nl.flotsam.preon.Codecs.decode(Codecs.java:218)
at nl.flotsam.preon.Codecs.decode(Codecs.java:199)
...
If I change size="recordLength" to a constant, e.g. size="42", I don't get the exception (but of course, then the record length always has to be the same).
Is there some other way for me to make the record length variable, or should I have organised things differently?
If anybody's interested, here's the JUnit test I've used:
package test.preon;
import org.junit.Test;
import static org.junit.Assert.*;
import nl.flotsam.preon.Codecs;
import nl.flotsam.preon.Codec;
import nl.flotsam.preon.DecodingException;
import test.preon.BinFile;
import test.preon.BinFile.Record;
import java.util.List;
public class BinFileTest {
@Test
public void parseBinFile() throws DecodingException {
Codec<BinFile> codec = Codecs.create(BinFile.class);
byte[] buffer = new byte[] {
2, 0,
3, 0,
'a', 'b', 'c',
4, 0,
'1', '2', '3', '4'
};
BinFile b = Codecs.decode(codec, buffer);
assertEquals(b.getNumberOfRecords(), 2);
List<Record> rL = b.getRecords();
assertEquals(rL.size(), 2);
Record r0 = rL.get(0);
assertEquals(r0.getRecordLength(), 3);
assertEquals(new String(r0.getData()), "abc");
Record r1 = rL.get(1);
assertEquals(r1.getRecordLength(), 4);
assertEquals(new String(r1.getData()), "1234");
}
}