I am quite new to Java and was trying to improve my skills a little by doing a binary file reader. The choosen one was the TLV format. I've chosen that one for being reasonably simple, but not so simple. Also I had some concrete examples with good documentation which I could follow for decoding.
The reader works, but I ended up with a class that seems way too big to be right. I wonder if anyone can break down this code into something more robust.
package tlv;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import tlv.errors.MissingI10ParameterException;
import tlv.errors.ParseI10ParameterException;
import tlv.errors.TLVFileException;
/**
* Represents a TLV file.
* Expects class File, which represents a TLV file formated as:
* [HEADER]
* [LogicalBlock-1]
* [DataSegmentHeader-1]
* [DataSegment-1]
* [DataSegmentTrailer-1]
* (...)
* [DataSegmentHeader-n]
* [DataSegment-n]
* [DataSegmentTrailer-n]
*
*/
public class DiriTlvFile {
ArrayList<LogicalBlock> dirisegs = new ArrayList<LogicalBlock>();
int tbc = 114;
public DiriTlvFile(File tlv) throws TLVFileException {
DataInputStream dis = null;
try {
// Here BufferedInputStream is added for fast reading.
dis = new DataInputStream(new BufferedInputStream(new FileInputStream(tlv)));
// Header data
byte[] ba = new byte[3];
/* This is the fixes header added
We just skip it straight away. */
dis.skipBytes(114);
LogicalBlock lb = null;
// Iterate over main file parts
while (dis.available() > 0) {
if (dis.read(ba, 0, 3)!= 3)
throw new TLVFileException("Error reading DataSegmentID (wrong byte count) @" + tbc);
tbc += 3;
String headerId = new String(ba);
// File header
if (headerId.equalsIgnoreCase("I01")) {
lb = new LogicalBlock();
// Skip uninteresting part of header
dis.skipBytes(50);
tbc += 50;
// Data Segment body
} else if (headerId.equalsIgnoreCase("I10")) {
if (lb == null)
throw new TLVFileException( "Logical Block was not initiated! (Missing I01 structure in file).");
byte[] dsheader = new byte[25];
if (dis.read(dsheader, 0, 25) != 25) // Head of the DS
throw new TLVFileException( "Error reading DataSegmentHeader (I10, wrong byte count) @ " + tbc);
tbc += 25;
int dssize = (dsheader[24] & 0xFF) - 1; // Size of DS body (+the header itself?)
byte[] dsbody = new byte[dssize]; // Body of DS
// Read DataSegmentBody
if (dis.read(dsbody, 0, dssize) != dssize)
throw new TLVFileException( "Error reading DataSegmentBody (I10, wrong byte count) @ " + tbc);
tbc += dssize;
// Create the datasegments
try {
lb.addDataSegment(new I10DataSegment(headerId, dsheader, dsbody));
} catch (MissingI10ParameterException e) {
// TODO - Improve this message using the mandatory parameter list from the exception
throw new TLVFileException("Error parsing DataSegment body (I10) : Missing one of mandatory parameters.");
} catch (ParseI10ParameterException e) {
throw new TLVFileException("Error parsing DataSegment body (I10) : Could not parse DataSegmentParameters.");
}
} else if (headerId.equalsIgnoreCase("I03")) {
// total records read
byte[] trailertotrecs = new byte[3];
// Have left the loop, means it found the footer
byte[] trailerbody = new byte[4];
if (dis.read(trailerbody, 0, 4) != 4) // Trailer Body
throw new TLVFileException( "Error reading Trailer Body (I03, wrong byte count) @ " + tbc);
tbc += 4;
System.arraycopy(trailerbody, 1, trailertotrecs, 0, 3);
// TODO - compare the number of records read with the information in the footer
dirisegs.add(lb);
} else {
throw new TLVFileException("Unknown Segment ID found");
}
}
// dispose all the resources after using them.
dis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public Iterator<LogicalBlock> getLogicalBlocks() {
return dirisegs.iterator();
}
public Iterator<DataSegment> getDataSegments(){
ArrayList<DataSegment> a = new ArrayList<DataSegment>();
Iterator<LogicalBlock> i = getLogicalBlocks();
while (i.hasNext()){
Iterator<DataSegment> j = i.next().getDataSegments();
while (j.hasNext()){
a.add((DataSegment)j.next());
}
}
return a.iterator();
}
}
P.S.: I've omitted the other classes as they are pretty much only byte-parsing.