XML
BCEL
XML Spy
Compiler
ANTLR
Java
VM
Tools
Apache
Eclipse

 |
|
<< Previous 1
2 3
4 5
As you can see, the code iterates through each entry in the array and reads the appropriate primitive based on the element type. That's simple, but what about aggregate types? Instead of defining the grammar as an array of types, we can define it using a class, which can contain primitive fields, arrays, and references to other classes. Since the input stream follows the grammar, the input stream is like an instance of the grammar. Just like an object is an instance of a class. So the parse method accepts a grammar (as a class) and returns an object. The object contains data read from the stream, which follows the grammar. This is an example of how meta data can be used avoid a lot of tedium. Here are a few examples of data and their respective meta data:
- object -> class
- language -> grammar
- XML document -> XML schema
- database row -> database schema
Rather pleasing symmetry, isn't it?
Now that we can read primitives, how do we read aggregate types such as arrays and structures? Arrays require two additional pieces of information: the length and the type for each element in the array. It turns out the length of arrays appears just before the array data in a class file, so
my choice was to save the value of the last primitive that was read in a field called
count. We obtain the array type by getting the name of class describing the array. For example, a single dimensional array of integers has a class name of
[I and an array of
Strings has a class name of
[Ljava/lang/String;. The count, along with the array type, is used to create an instance of the array. The parser proceeds calling
parse() recursively to read
count elements and populate the array.
Handling structures is similar, but each field can contain different types. For a given class, we simply create a new instance of the class and iterate through its fields, call
parse() recursively and set the value for that field. The constant pool is a little tricky because it's an array of different types of structures. The type of structure is defined by a type byte preceding the data for each structure. It sounds like a job for an abstract class with concrete subclasses for each structure type. We just need a way to map the type byte to the appropriate subclass.
My choice was to define two static fields in the abstract class for that mapping. It looks like this:
public abstract class CPEntry {
static Object[] SUBTYPES = {
new Byte((byte) 1), Utf8Entry.class,
new Byte((byte) 3), IntegerEntry.class,
new Byte((byte) 4), FloatEntry.class,
new Byte((byte) 5), LongEntry.class,
new Byte((byte) 6), DoubleEntry.class,
new Byte((byte) 7), ClassEntry.class,
new Byte((byte) 8), StringEntry.class,
new Byte((byte) 9), MemberRefEntry.class,
new Byte((byte) 10), MemberRefEntry.class,
new Byte((byte) 11), MemberRefEntry.class,
new Byte((byte) 12), NameAndTypeEntry.class,
};
static Class TYPECLASS = Byte.TYPE;
}
The SUBTYPES array defines the mapping for each type value, TYPECLASS defines the size of the type identifier itself, which is a byte. Now we need to declare classes for each of the subclasses. Here are a couple of examples:
class Utf8Entry extends CPEntry {
short length;
byte[] data;
}
class IntegerEntry extends CPEntry {
int value;
}
Next: Java code for the parser
1
2 3 4
5
Next>>
|
|
|