Argot Tutorial

This tutorial covers the creation of a very simple dictionary used in the bookstore application that comes with the download. It also shows how this dictionary is used in an application to read and write a simple list of books to a file.

To start, we need to define a file format which we are going to use to store all our book descriptions. The bookstore we are developing will use a very simple file format to store all the details of the books we use.

Argot allows us to model any binary data format. In this case will use a simple array of book descriptions to describe the whole file. So lets look at our first Argot description:

booklist: 
   {
	meta.array( 
	   @u8["size"],
	   @book["book"]
	)
   };

This first Argot description describes a booklist. A booklist is defined as an array of books using an unsigned 8bit value to describe its size.

The meta.array data description takes two values. The first value specifies the size of the array, and the second value specifies what type of data is used in the array. The @ symbol is used to denote a reference to a data type. The ["size"] simply names the references so we know what we're using the data to describe.

The alert readers will notice that using an unsigned 8bit value to record the size of the array, this will mean we can only store a maximum of 256 books in the file. For the purposes of this example, that will be plenty. Anymore and we would obviously store our books in a database.

Now we have defined the file format as being a simple array of book elements. To finish off the file format we also need to define the format of a book. The Argot specification for a book looks like:

book: 
   {
	@u8ascii["ISBN"],
	@u8ascii["title"],
	@u8ascii["description"],
	@u8ascii["author"]
   };

Once again this is an overly simplified example of the structure of a book. The specification describes a book as a sequence of u8ascii elements for ISBN, title, description and author.

The u8ascii format is a common data type which describes a ascii encoded string with the size described with a u8. The Argot definition is:

u8ascii: 
   {
	meta.encoding(
	   meta.array(
		@u8["size"],
		@u8["data"]
	   ),
	   "ISO646-US"
	) 
   };

Once again we are using a short ascii description, allowing each field to use a maximum of 256 characters.

The file format description is now complete. Using the Argot compiler we are able to generate a dictionary file which describes these formats in Argot's native binary format.

Note: The Argot dictionary format is itself described using Argot. In fact, the Argot dictionary file format is totally reflective. Every element described in the dictionary is also described in the file.

We need load our dictionary into the Argot runtime system and bind the marshallers to each data type.

void setupArgot() 
throws TypeException, IOException
{
	TypeLibraryLoader libraryLoaders[] = {
		new MetaLoader(),
		new DictionaryLoader(),
		new CommonLoader(),
		new BookstoreLoader()
	};
		
	TypeLibrary library = new TypeLibrary( libraryLoaders );
}

We create a TypeLibrary by passing in an array of loaders. A loader will read a dictionary file and bind the languages data types to the dictionary data types. The MetaLoader is used for the meta data types. The DictionaryLoader binds the format of dictionary files. The CommonLoader binds common data types. And finally the BookstoreLoader binds the data types used in the bookstore dictionary. For example the BookstoreLoader binds the following:

public void bind( TypeLibrary library ) 
throws TypeException
{
   library.bind( "book", 
	new TypeBeanMarshaller(), 
	new TypeBeanMarshaller(), Book.class );
   library.bind( "booklist", 
	new TypeArrayMarshaller(), 
	new TypeArrayMarshaller(), Book[].class );
}

The book type is marshalled with the Bean marshaller. Any object corresponding to a Java Bean interface can be marshalled with this. The booklist type is marshalled with an array marshaller.

With marshallers configured, we can now start reading and writing our file format.

public Book[] loadBooks(String filename)
throws IOException, FileNotFoundException, TypeException
{
   FileInputStream inputStream = new FileInputStream( filename );
   TypeMap map = new TypeMap( TypeSystemLibrary.getDefault());
   map.map( 1, library.getId( "book" ) );
   map.map( 2, library.getId( "booklist" ));
   map.map( 3, library.getId( "u8utf8"));
   map.map( 4, library.getId( "u8"));
            
   TypeMimeInputStream typeInputStream = 
	new TypeMimeInputStream( inputStream, map );
   return (Book[]) typeInputStream.readObject( "booklist" );
}

And to write books to a file, our method will look like:

public void saveBooks(String filename, Book[] books )
throws IOException, FileNotFoundException, TypeException
{
   FileOutputStream outputStream = new FileOutputStream( filename );
   TypeMap map = new TypeMap( TypeSystemLibrary.getDefault());
   map.map( 1, library.getId( "book" ) );
   map.map( 2, library.getId( "booklist" ));
   map.map( 3, library.getId( "u8utf8"));
   map.map( 4, library.getId( "u8"));
                
   TypeMimeOutputStream typeInputStream = 
	new TypeMimeOutputStream( outputStream, map );
   typeInputStream.writeObject( "booklist",  books );
}

The TypeMap is an important aspect of Argot. It allows us to map and constrain the data types we use in a file. It will be covered in more detail later.

We now have a complete Argot solution to reading and writing the booklist to a file format which is completely system independant. The format we used contains just the array of books as we defined.

The complete bookstore application is included in the Argot download. Visit the download area to download Argot.

The Colony tutorial continues the bookstore application. It demonstrates how the marshalling code written for the file allows us to now publish and move books in remote communications with no additional work.

Copyright 2004-2007 © Einet Pty Ltd
Legal Notice