2010-03-02 47 views
0

我试图使用Preon来解析二进制文件,它们被构造为一系列可变长度记录。对于每条记录,都有一个指定记录长度的数字(以字节为单位)。解析Preon中的变量记录长度

这里是什么,我试图做一个简化版本:

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; 
     } 
    } 
} 

所以,numberOfRecords指定文件中的记录数和RECORDLENGTH指定每个记录的长度。问题是Preon无法解决Record中的recordLength问题,尽管在BinFile中numberOfRecords可以正常工作。

这里是例外,我得到:

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) 
    ... 

如果我改变大小= “RECORDLENGTH” 为一个常数,例如大小=“42”,我没有得到例外(但当然,那么记录长度总是必须相同)。

有没有其他的方式让我做记录长度变量,或者我应该组织的东西不同?

如果任何人的兴趣,这里的JUnit测试我使用:

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"); 
    } 
} 

回答

1

原来你遇到了一个错误。 ListCodecFactory有一个策略用于决定在各种情况下生成哪种类型的编解码器,并且事实证明它在这种情况下选择了错误的编解码器。我有它的补丁,如果你有兴趣,我可以发给你。

+0

创造了在这里的bug报告:附加一个补丁,这个问题http://kenai.com/jira/browse/PREON-9 – 2010-03-04 06:33:20

+0

。 – 2010-03-04 06:41:02

+0

太棒了!谢谢。 – skoob 2010-03-04 07:19:23

1

创建了两个错误报告:PREON-16PREON-17。第一个解决了上述问题。第二个解决了一个稍微相关的问题,这里值得一提:

在下面的代码中,名为'records'的列表中的Test2元素的大小完全由Test1定义。 (Test2的大小基本上是Test2.value中的字符数,由Test1的'nrCharacters'属性确定。)

因此,Preon能够优化读取记录列表。它不必一次读取所有记录;相反,它只能在需要时跳过它并阅读这些元素。 (元素的起始位置基本上是元素索引的函数。)

然而,难点在于在读取Test2的实例之前需要计算元素的大小。由于它包含基于Test2上下文的引用,因此这些引用需要重写。事实上,整个大小的表达式(尽管在这种情况下是一个简单的表达式)需要重写。这在PREON-17中解决。

public static class Test1 { 

     @BoundNumber(size = "8") 
     public int nrRecords; 

     @BoundNumber(size = "8") 
     public int nrCharacters; 

     @BoundList(size = "nrRecords", type = Test2.class) 
     public List<Test2> records; 

     public static class Test2 { 

      @BoundString(size = "outer.nrCharacters") 
      public String value; 

     } 

    }