The Odyssey : An Introduction To Cut The Crap Software

Author: Martyn Cutcher

email martyn@cutthecrap.biz

website www.cutthecrap.biz

This document attempts to take you on a journey that explains the rationale behind the Cut The Crap software, and how it makes significant advances in a number of software areas.

Cut The Crap

"Cutting The Crap" is not just about making things simpler.

It is about addressing and solving the right problem at the right time. Understanding the nature of the systems we are developing.

In the same way that end users need a consistent application model for them to be effective, so developers need consistent, powerful and applicable models that they can utilise to build effective systems.

How To Use This Document

The Cut The Crap software packages are related at a number of different levels.

Whilst it may be possible - and is attempted elsewhere on this site - to explain each in isolation the best appreciation is gained through an understanding of the full set of technologies.

It therefore seems reasonable to attempt a single sequential presentation. Providing a more guided introduction to the software.

It is hoped that anyone - from interested school kids and journalists to professional IT people - should be able to follow this guide - this Odyssey - and discover some understanding of the technologies available.

It is further hoped.... that some will.

Close To The Metal

Sections like this will discuss more technical issues that may not be of interest to all.

What YOU Have To Do

This is an interactive journey. There are some things that you will have to do. You should accept right now that there may be some problems that will take you some time to think through before you can move on to the next stage.

Setting systems up - configuration - is often a pain, and few people, if any, enjoy it.

But, do not worry. There is nothing here deliberately to catch you out, and if you follow the instructions and leads given - and stay relaxed - it is hoped that the journey will be tolerable at least, and enlightening at best.

Where You Are Going

The journey has already begun since you are reading this document.

The next stage is to download and setup the Cut The Crap software and a number of other packages. It is important that all this is done correctly otherwise you will face unnecessary problems later on.

You will then be lead through a set of interactions with the different Cut The Crap technologies, from the storage packages through to the latest InterActor graphical tools.

If you have any problems - or comments - please email me.

The Setup

Getting Started

You'll need to download the latest Cut The Crap software. This download includes all required packages for this document.

Create a target directory and unzip the downloaded file into it.

Now is a good time to set the environment variable CTC_ENV to refer to the install directory, eg F:/test/cutTheCrap

Check your Java environment

The Cut The Crap software is all written in Java - a cross platform environment. You need to see whether your system has a good version installed.

Open a console window - on Windows this will be from the menu :

Start-Programs-Accessories-Command Prompt

Now enter :

java -version

You should see one of two responses.

'java' is not recognized as an internal or external command

..or

java version "1.4.1_01"

where the version number reported could be anything from "1.0" onwards.

If you have any response other than a reported version number greater than or equal to "1.4", you must download and install a new version of java.

If you continue with an older version of java there will be unpredictable consquences and horrible error messages.

Downloading and installing java is free, but may be time consuming on slower connections. The latest versions can be downloaded from here.

Although you can initially get by with the JRE - Java Runtime Environment, you will eventually need the full java SDK - Software Development Kit.

If you are not already a java developer it is unlikely that you will have the development kit installed. So save yourself some pain later on and download and install the latest development kit now. You will have to fill in a simple registration form with Sun to download the software, but this is quite straightforward.

Required 3rd Party Jars

The Cut The Crap software requires three other jar files. The internal logging system uses log4j, a number of elements a sax parser, and the Alchemist utilizes the Apache regexp package. The three required jars are included in the distribution, but latest versions, with full documentation and source code can be downloaded from :

Test The Cut The Crap jar file

Okay, so you have a valid java environment, right?

Locate the directory where you extracted the Cut The Crap zip file and make this the current directory in your command window.

On windows you can change disk drives by entering the drive letter followed by a ':' (colon). For example D:. You can then "change directory" using the cd command to navigate to the chosen directory.

Enter :

ctctest

You should see some message confirming that all is okay. If not, then confirm again that you have the correct java version and that you have entered the command correctly .. and that you are in the right directory.

On a unix system you should find a ctctest.sh file. You can rename this ctctest and ensure it is executable :

mv ctctest.sh ctctest;chmod +x ctctest

This applies similarly for other commands later on.

Jython Environment

The best kind of environments in which to learn are those where you can make mistakes and try things again really easily. For this journey the Jython environment is used. Jython is a version of the Python programming language written in Java that makes it very easy to try out interactions with Java programs - precisely what we need here.

So, if you don't already have Jython installed you'll need to download and install it following the instructions provided. You can get it from here. Don't worry, it is a pretty small download - compared with the Java SDK in any case.

Once installed, you may need to add the jython directory to your path. If unsure about how to do this, then check out the howTos section for either windows or unix section.

If you have changed the path variable, you will have to open a new command window in order for it to have access to the modified variable.

Setting Out

In the distribution, along with the jar file and the javadocs are a few other files which are included to help you.

A couple of these, jytest.bat and ctcenv.py are specifically to help with jython experiments.

Enter

jytest

Jython should start up and leave you with a >>> prompt. If you get an error you should check your current directory and confirm that jython is on your path - just enter jython to confirm that it can start up.

The jytest command file simply loads jython and executes the ctcenv.py script that imports the various Cut The Crap packages. Have a look at the files to see what they are doing. They are quite straightforward.

The ctcenv.py includes a line import sys;, allowing the interactive call to sys.exit() as the best way to exit the jython environment tidily.

Enter

sys.exit();

You should now be back at the standard command prompt. Its always good to know how to quit a system.

System Configured!

Okay, if you've got this far you are ready to start exploring the Cut The Crap software properly. If you've never done anything like this before, then well done! Everything else should be a breeze.

The Storage Package

Welcome to Cut The Crap

First things first, start up the test environment by re-entering

jytest

Remember, that if you want to exit at anytime enter sys.exit();. The ';' character is optional but personally I always use it to indicate the end of the statement.

The first package to investigate is the storage package.

Computers are fundamentally about the storage and manipulation of data. However data is retrieved and manipulated, at some point some data has been accessed in some format from permanent storage. Computer programs themselves are simply data that has been retrieved by some system and then used to provide instructions to a computer processor.

So storage and retrieval is a fundamental aspect of computing.

Right, at the >>> prompt enter :

>>> store = RWStore("/ctc/test/store.rw", 0);

If you are a Java programmer new to Jython then you should know that there is no need for the new keyword in Jython. If you are not a Java programmer - don't worry.

You should be able to find the created file on the file system using a file browser. If necessary, the software will have created the required directories to build a valid path.

You will find that a lock file has also been created. This is used to prevent multiple java applications trying to update the same file.

The store package includes several simple helper classes, one of these is the StringStore that helps save and retrieve simple strings.

>>> sstore = StringStore(store);
>>> s1 = sstore.save(0, "Some Text");

To store a string, and:

>>> sstore.read(s1);
'Some Text'

... to retrieve it.

The variable s1 is set to a reference returned by the save method that can then be used to retrieve the original string using the read method.

If you've not done any programming before, or feel the syntax is a bit strange, just take a while to look at what has been written - hopefully it will start to make a little sense. Remember that x=y; sets the variable x to the value of y

Reallocation

An equally fundamental feature to data storage and retrieval, is storage reallocation.

This means that some data value needs to be updated. In the simplest scenario, the same data area that stored the original values, might simply be overwritten. But this is not sufficient in general.

Consider the case that our original string "Some Text" should be replaced by "Some More Text". With the emphasis on the "More".

Since the second string is a different size to the first it is not sufficient to simply overwrite it. This is the difference between "reallocation" and simple "update".

You may have noticed that the save method took two parameters, sstore.save(0, "SomeText");. The first parameter was a data reference to be reallocated, and '0' was a special value that indicated no existing reference.

If we want to update - reallocate - our string we should enter the following:

s2 = sstore.save(s1, "Some More Text");

The new variable s2 now contains the reference of the updated string and the original reference s1 can be reused for some other data later on.

Transactions

When using a word processor and working on a document, every now and then - if you are sensible - you'll save the file. This hopefully will ensure that a system failure of some kind won't mean that you'll lose all your hard work. But what you wouldn't do, is save your document after every key stroke.

A Cut The Crap store file might be several gigabytes in size. So it is simply not a scalable approach to hold the whole store in memory and save it all to disk every now and then.

Instead the storage structures allow for incremental changes to the store to be made efficiently and in a constant time independent of the size of the store file.

With such a system comes the idea of a Transaction. A Transaction is an "update unit", in other words, the store file will be modified in chunks, and these "chunks" are called Transactions.

A Transaction provides a scope, or context, within which a number of separate changes can be made together.

Transaction processing is a fairly developed concept in computing and comes with some additional terminology. Transactions are started and then either commited or rolled back, which is a bit like "revert to saved" in a word processor.

In any case, this is a good place to think about what Transactions are.

Store Transactions

The Cut The Crap storage package defines a set of functions to control a transaction.

These are simply startTransaction(), commitTransaction() and rollbackTransaction().

The utility StringStore class already used, internally started and committed transactions whenever it saved a new string.

The Cut The Crap transaction implementation allows for nested startTransaction and commitTransaction requests. Only performing a "real" commit when the transaction has been "unwrapped" completely.

You can create your own transaction scopes by entering:

>>> store.startTransaction();

...save some strings, and then enter:

>>> store.commitTransaction();

This probably doesn't seem very exciting just now. But the relevance of the Transaction model will become apparent later.

Another Simple Store

The StringStore was a useful little helper to introduce the idea of storing and retrieving simple - string - values.

Another helper class is the ObjectStore demonstrating that the store can be used to contain different kinds of data.

Trying out the ObjectStore will also introduce some ideas that are developed much further by the Cut The Crap Generic Persistant Object system - GPO - later on.

Enter the following:

>>> obstore = ObjectStore(store);

It turns out that the obstore appears to do pretty much the same as the StringStore, it just does it more generically.

It achieves this by storing all data as Java objects, where the StringStore only dealt with Java Strings.

>>> s3 = obstore.save(0, "A Java String Object");
>>> obstore.read(s3);
'A Java String Object'

But the ObjectStore can be a little more useful. For example, it could store a java HashMap object.

>>> hm = java.util.TreeMap();
>>> hm.put("value", s3);
>>> r1 = obstore.save(0, hm);

A HashMap object allows values to be stored against a specified "key". In the example above, the key specified is the string "value" and the value is the reference to the string object saved earlier.

We shall now use a new method. setRootAddr on the store object allows us to save a single reference with the store. When we re-access the store later on, we can retrieve this value using getRootAddr, here is how it works:

>>> store.setRootAddr(r1);

Now, we can write:

>>> root = store.getRootAddr();
>>> rootOb = obstore.read(root);
>>> obstore.read(rootOb.get("value"));
'A Java String Object'

Some of you will think this is all pretty straightforward, whilst others will probably already be really confused. If you are confused, don't worry, just go over the document and take a while to think about it.

The point of this last example is to show how a simple object store can be built over the basic storage package. Shortly you will examine the Cut The Crap Generic Persistant Object system which takes things on a few levels, but is still built on the same storage system.

Close To The Metal - Streams

The two utility objects we have used so far have made it easy to demonstrate that data can be saved to and retrieved from the store.

These utilities use an underlying IStore object.

This interface uses stream objects and this is a good point to have a look at what this means.

A stream in computing is some object that supports the reading or writing of a sequence - a stream - of bytes. For example, the string "Hello World" would be written to a stream by writing each character in turn, 'H', 'e', 'l', etc.

The IStore object will provide an output stream when a request is made to reallocate an address. Data is then written to the stream and the stream is saved to the store. Here is a code fragment from the StringStore.

public int save(int oldAddr, String str) {
  m_store.startNativeTransaction();
	
  byte bytes[] = str.getBytes();
	
  PSOutputStream psout = m_store.realloc(oldAddr);
  psout.writeInt(bytes.length);
  psout.write(bytes, 0, bytes.length);
	
		
  int retval = psout.save();
	
  m_store.commitNativeTransaction();
	
  return retval;
}

The PSOutputStream and PSInputStream objects provide additional utility methods to support the direct writing of other stream objects.

Suppose we have an MP3 file "/music/fav.mp3", in our jython environment :

>>> store.startTransaction();
>>> os = store.realloc(0);
>>> os.write(FileInputStream("/music/fav.mp3"));
>>> fav = os.save();
>>> store.commitTransaction();

and we can retrieve the data and write to another file like this:

>>> is = store.getData(fav);
>>> is.read(FileOutputStream("/music/fav2.mp3"));

A Slightly Different Store

You are now going to take a quick look at the Cut The Crap "Write Once" Store.

First let's start a nice clean session:

>>> sys.exit();

and

jytest

The write once store is accessed using a different object, previously you used RWStore - for "Read Write Store", this time you'll use the WOStore.

>>> wos = WOStore("/ctc/test/store.wo", 0);

"Write Once" means that it does not overwrite already stored data, enter the following:

>>> os = ObjectStore(wos);

>>> s1 = os.save(0, "First String");
>>> s2 = os.save(s1, "Second String");
>>> s3 = os.save(s2, "Third String");

You can now read each of the previous values back as usual, using s1, s2 and s3 since no data has been overwritten. Try it and see.

This is maybe not so interesting in itself. More useful is that because the call to save - and more importantly the underlying call to realloc passed in the old address, this chain of values can be retrieved.

>>> os.read(wos.getPreviousAddress(s3));
'Second String'

This may seem like a simple idea - and it is - but it is extremely powerful.

Using the RWStore the method getPreviousAddress will always return 0.

The Object Model

Generic Persistant Objects - GPOs

Hopefully, with experience of the storage package you will have some appreciation that data, for it to persist, must be stored and retrieved in some way.

With the GPO model, the persistance mechanisms are hidden from the programmer. The programmer does not make calls to the underlying storage package. Instead a new object, the Object Manager provides services to manage object references.

The Object Manager is not magik. It stores special data structures in the store that allow it to match object references against storage references, maintaining a crucial property - Object Identity. Let's see how this works.

First, exit the current jython session:

>>> sys.exit();

..and start a new one by entering:

jytest

So we have a nice new session without any old values lying around that might confuse us.

The first object we will use is OMClient, this helps us to initialize the system, and provides us with an Object Manager. Enter :

>>> client = OMClient("", "/ctc/test/omstore.rw");
>>> om = client.getObjectManager();

These two lines have first of all initialized a system that uses the specified file as the storage file for a new Object Manager and then returned that Object Manager, setting the variable om.

From now on you forget about the store, only the Object Manager is important to you.

A First Persistant Object

The next important object you will use is the GPOMap. Its importance cannot be overstated so it is probably best not to discuss it. Let's create one:

>>> g1 = GPOMap(om);
>>> g1
1

Note that to create a GPOMap object we needed the om variable that reference the Object Manager, another option would allow another GPOMap object to be passed instead, indicating that the new object should be stored with the existing object. This is not something that should concern you for now however.

Okay, you can set some values:

>>> g1.set("name", "first object");
>>> g2 = GPOMap(om);
>>> g2.set("name", "second object");
>>> g1.get("name");
'first object'
>>> g1.set("friend", g2);
>>> g1.get("friend").get("name");
'second object'

Do you understand what happened here? If it all seems a bit much, just try to understand each individual statement and think about what is being requested.

You have created two objects - g1 and g2 - given each a "name", and made g2 the "friend" of g1.

The final request was to get the "friend" of g1 - (g2) - and then to get its "name".

You Looking At Me?

If Travis Bickle had been a GPOMap object he would have known for sure.

If a GPOMap object references another, then that object knows about it.

>>> ls = g2.getLinkSet("friend");
>>> ls.size();
1

This tells you that the set of GPOMap objects that reference g2 with the "friend" property has one member.

>>> ls.getFirst().get("name");
'first object'

Persistant?

You don't believe me? Try this.

>>> om.remember("G1", g1);
>>> sys.exit();

Now start the session as before:

jytest
>>> client = OMClient("", "/ctc/test/omstore.rw");
>>> om = client.getObjectManager();
>>> g1 = om.recall("G1");
>>> g1.get("name");
'first object'
>>> g2 = g1.get("friend");
>>> g2.get("name");
'second object'
>>> g2.getLinkSet("friend").getFirst().get("name");
'first object'

The remember and recall methods should only be used for a very few objects. In many systems only a single object needs to be remembered in this way, all others can then be navigated from this one.

Associations

When one object has a reference to another, this is described as an object Association.

The previous example indicates that for every property there is a set of objects that might use that property to reference another object. Therefore any property can be used to define a many-to-one association.

All associations have a "direction", "I'm your friend, will you be mine?".

>>> g3 = GPOMap(om);
>>> g3.set("name", "third object");
>>> g3.set("friend", g2);
>>> g2.getLinkSet("friend").size();
2

It would seem that g2 is starting to get popular. The LinkSet can be examined using an iterator object, here is some Jython that does this.

>>> iter = g2.getLinkSet("friend").iterator();
>>> while iter.hasNext() :
>>>   print iter.next().get("name");
>>>
'first object'
'third object'

You have to be careful here. After the while statement you use a ':' colon character. Use 2 (two) spaces before the print line, and then hit enter twice after completing the line. This is needed because the python syntax is based on indentation to define blocks of code, and the ':' character indicates a following code block.

Close To The Metal - Many To Many

Clearly a one-to-many association structure will support a requirement for a one-to-one association. But what about many-to-many? From our example, it would appear that although g2 can be a "friend" of many objects each object may only set a single object as their "friend".

To setup a many-to-many association an intermediate object is required. This may seem like its getting complicated, but that is the nature of this problem.

To help manage the configuration issues, a utility class ManyManyLink is provided.

>>> ManyManyLink("aFriendOf", g1, "aFriendTo", g2);

Which can be read as :

g1 is "aFriendOf" g2
g2 is "aFriendTo" g1

Let's add a couple more:

>>> ManyManyLink("aFriendOf", g3, "aFriendTo", g2);
>>> ManyManyLink("aFriendOf", g1, "aFriendTo", g3);

Okay, it would appear that we have managed to stitch the objects together, but how can they be accessed?

>>> ls = g1.getLinkSet("aFriendOf");
>>> ls.size();
2
>>> ls.getFirst().get("name");
'second object'

Okay, so what happened there? The LinkSet recognized the ManyManyLink object and automatically "resolved" the set members through the links it managed. So you can consider access to a "many to many" association in the same way as a "one to many" association.

Since the ManyManyLink object is used in this special way, it is declared as final so that its use cannot be modified.

To make things even easier, there is a utility method on IGPOMap to create the ManyManyLink implicitly, we could have achieved the same by:

>>> g3.addManyMany("aFriendOf", "aFriendTo", g2);
>>> g1.addManyMany("aFriendOf", "aFriendTo", g3);

Light Relief

You'll be glad to know that that completes everything on object associations.

Classifications

The LinkSet objects that result from object associations are now addressed.

Suppose the LinkSet contains ten million members. How is such a set managed?

If it is necessary to be able to access specific members of a set, traditionally this would be done by some kind of lookup function.

"Find the member of set A with the name 'Blah'."

This is managed using an object called a Classifier, used like this :

>>> om.registerClassifier("link", "value");
>>> p = GPOMap(om);
>>> c1 = GPOMap(om);
>>> c1.set("value", "C1");
>>> c1.set("name", "just another object");
>>> c1.set("link", p);
>>> ls = p.getLinkSet("link");
>>> lkup = ls.getClassifier("value");
>>> lkup.getValue("C1").get("name");
'just another object'

The classification structure maintained is very efficient and can be used to index extremely large sets of objects.

Classifiers know about ManyManyLink objects and process them transparently. Relational database experts might like to think what the equivalent of this would be.

Formulas

A recent significant enhancement to GPO is the ability to define spreadsheet-type formulas. By "spreadsheet-type" I mean that values are kept uptodate by recalculations triggered on values on which they depend changing. This will be an extremely brief introduction.

Firstly, a formula is always calculated within the context of some object. Property values of the object are accessed using a lookup function. Have a look at this example:

>>> g = GPOMap(om);
>>> g.set("value", 10);
>>> g.define("vat", "(* 0.175 (-> value))");
>>> g.get("vat");
1.75

Note that the expression (-> value) returns the value of the property "value".

Now if we modify the value, the "vat" will be re-calculated:

>>> g.set("value", 20);
>>> g.get("vat");
3.5

Formulas can be combined quite simply:

>>> g.define("total", "(+ (-> value) (-> vat))");
>>> g.get("total");
23.5

A number of formular primitives - such as * and -> are provided as standard, but mechanisms are available to define new primitives in a straightforward way. You can see the current list of built-in primitives here.

GPO Summary

There has not been much explanation here. Each section demonstrates a direct solution to a specific problem or requirement. The examples used have been relatively simple but be in no doubt that the techniques demonstrated have been designed from the beginning with scalability in mind.

There are many other aspects that could be presented and discussed, but the essential information is here.

The Alchemist

The Alchemist generates java code that exploits the GPO model from a minimal system specification.

"Minimal" means just that. The "minimal" amount of data needed for the Alchemist to infer the intended system is entered. This is not magik, but it is smart.

We have already seen in the storage and GPO packages that significant aspects of computer systems can be reduced to a number of relatively simple patterns. The next stage is to determine higher level system characteristics and see whether these follow deterministic (predictable) patterns.

Java Source Code

The Alchemist generates Java source code that must be compiled to provide a working system. To help in this process, files are also generated that can be used by the ant tool to manage the compilation of the system.

You will therefore need to to have installed on your computer not only the java SDK (rather than just the JRE), but also ant. This can be downloaded from here, it about 7Mb in size.

The download is a zip archive that should be expanded, the next task is simply to add the ant bin directory to you path system variable. Once this is done you should be able to enter ant -version at a new command window, and get a meaningful response.

Initial Framework

Before we go any further, you'll need to setup a system environment variable CTC_ENV. This should be set to the fullpath of the cutthecrap directory downloaded. It should include the name of the directory itself - in case you have changed the name, for example F:\test\cutthecrap.

In this directory you will find a src directory and within that an alchemy folder.

This contains a single build.properties file and a contacts directory. The contacts directory contains a file - contacts.orm.

The console generation process will be driven by the Ant tool, and for this we will need a gen.xml file. Since it is always error prone to write this kind of thing for yourself, the Alchemist provides a utility to generate the file that will be used to generate the system.

From the current directory enter :

..\gengen contacts

This should have created the required gen.xml file. If you're interested you could take a look at it.

The contacts.orm xml file contains all the xml definitions needed for this exercise, but you will "unwrap" the functionality by "uncommenting" areas of the file as we proceed. Another option would have been to include a number of copies of the file in different states, but I thought if anyone got this far they would be happy to edit a file.

The Contacts Example

The system that will be generated can be used to maintain a list of contacts, like an address book.

Each contact will correspond to a single individual and will have a number of simple properties: name, email, phone number ... and so on.

We will then add some more structure by introducing the concept of a group that a contact may be a member of.

After each stage, you will be encouraged to examine the generated code, and to start a jython session to play with the system.

First Generation

Open a command window and make the contacts directory the current directory.

Enter :

ant -f gen.xml

You should see some status output as first any existing generated code is removed, new code generated, and then compiled.

If you now enter :

ant docs

The javadocs for the generated system will be produced. You will find them in the directory products/contacts/docs under the directory referred to by the CTC_ENV environment variable.

Startup a new jython test environmemt:

jytest

and use the first generated system:

>>> from alchemy.contacts.client import *;
>>> client = ContactsClient("", "/ctc/test/contacts.rw");
>>> book = client.getAddressBook();

This is in fact all that you can do at this stage. The only behaviour described in the orm file is that there is some class AddressBook that is the root object of the contacts system. You can confirm this by entering the following:

>>> book.getClass().getName();
'alchemy.contacts.AddressBook'

Okay, that is enough of that, quit the test environment with sys.exit();.

Open the contacts.orm file in an editor - notepad is fine if you insist - and have a peek.

If you are unfamiliar with <xml> syntax do not worry. It is a little ugly but does make sense after a while. For this exercise you just need to know that the <!-- and --> characters are used to delimit a comment. You will see these characters throughout this file and it should be apparent what the intention is.

The only active - uncommented - part of the file at the moment is the orm element that defines the package name to be generated, and the class element defining the root object.

You should now 'activate' stage two, by removing the comment character sequences that delimit the next two elements - the class and assoc elements.

Save the file, and rerun the generator. It might also be interesting now to take a proper look at the javadocs generated by :

ant docs

Locate the projects/contacts/docs directory and open the index.html file. Your package is contacts and you'll be interested in the AddressBook and Contact classes. You might checkout the ContactsClient class in the client package also.

Things can get a little more interesting now:

jytest

First you need to import the ContactsClient so we can get started:

from alchemy.contacts.client import *;

Now you should be able to get into the system proper:

>>> client = ContactsClient("", "/ctc/test/contacts.rw");
>>> book = client.getAddressBook();
>>> c1 = book.createContact("First");

Okay, to explain.

Why does the client have a method getAddressBook?

Because the AddressBook object was defined as the root of the system, and the client's task is mainly to provide access to the root object.

Why does the AddressBook have a createContact method, and why does it take a single argument?

Because the AddressBook object was defined as the owner of the Contact objects and the 'Name' attribute of Contact was defined as required, so must be provided in order to create the object.

If you look in the javadocs or the source code directories, you will see a class - IContactIterator. The Alchemist generates typed iterators that provide additional methods to the standard Iterator interface.

In IContactIterator you will find a nextContact method that will return a Contact object.

If you look more closely you will also see that it extends the GPO class IStriterator which supports a number of interesting features - explained elsewhere.

Stage 3

Exit the test environment again, and re-edit the contacts.orm file to uncomment the last block of definitions.

This last block defines the Group class and its associations with both the AddresBook and Contact classes.

Save the file and re-generate the system from the command line.

Now you are able to createGroups and add Contacts to a group.

You are also able to getGroups from the AddressBook, getGroups from a Contact - to see which Groups a Contact is in, and also getContacts from a Group to see which Contacts are withing each Group.

Checkout the regenerated javadocs and have a play in a new test environment.

I hope that this small demonstration has convinced you of the possibilities.

InterActor

The following interactive demonstration of InterActor should be a nice break from the earlier activities.

The demonstration of the InterActor model is made by controlling a display structure interactively using both the jython console and the generated graphical interface.

So exit any existing session and start a new one with jytest.

Start up an InterActor window by entering:

>>> ia = Helper.startNew(0);

This should have created a new system window titled "InterActor". The 0 parameter indicated that the java system should not exit when the window is closed - since we are controlling this from jython. Let's carry on :

>>> root = ia.getRootShape();
>>> root.getClass().getName();
'cutthecrap.ia.IAShape'

The display structure of InterActor is defined by two types of objects, IAShapes and IASlots. As the exercise progresses their use should become clear.

>>> w1 = ia.createWindow("Tester", 170, 100);
>>> w2 = ia.createWindow("Other", 170, 100);

These windows can now be displayed by adding them to the root shape.

>>> root.addChild(300, 30, w1);
IASlot
>>> root.addChild(300, 150, w2);
IASlot

You should see each window appearing in the InterActor window as you add them. Note that addChild returns an IASlot object.

addChild is a utility method that creates an IASlot object at the position given, and then sets the contents of the slot to the IAShape provided.

We'll now create another slot directly:

>>> slot = root.createSlot(50, 50);

and we'll create and set a Transition.

>>> trans = ia.createVenetian(15, 500);
>>> trans.addPhase(ia.makeColor(120, 120, 120));
>>> slot.setTransition(trans);

Now we'll set an alpha transparency value that will be applied to anything that is placed in the slot.

>>> slot.setAlpha(0.6);

See what happens when you enter the following:

>>> slot.setContents(w1);

That was a bit of a distraction, we'll now create some "radio" buttons, using a generic 2State class. A utility method create2StateLink defines buttons that when actioned will set the contents of some IASlot to some IAShape.

>>> tlink = ia.create2StateLink("Tester", slot, w1);
>>> olink = ia.create2StateLink("Other", slot, w2);
>>> elink = ia.create2StateLink("Empty", slot, None);

Cluster them with each other so that they ensure only one is on.

>>> tlink.clusterWith(elink);
>>> tlink.clusterWith(olink);

Now we'll sprinkle them around a bit, this'll look a bit odd but will demonstrate a few things.

>>> w2.addChild(20, 20, tlink);
>>> w1.addChild(20, 20, olink);
>>> root.addChild(20, 270, tlink);
>>> root.addChild(100, 270, olink);
>>> root.addChild(180, 270, elink);

Okay, click away and see what you see.

Tying It Together

The Alchemist generates a functional model at the system level. A programming interface is generated that allows programs to be written that interact with the generated model.

By itself this is a huge step forward.

But once we have begun to gain a generic appreciation for the way that systems can be used, it seems we can go further.

One of the elements generated by the Alchemist is a method called getMetaSpec.

The MetaSpec returned by this method produces a description of the object. And this description is sufficient to consider the generation of a "user level" graphical interface. One that is usable, scalable, reliable and consistent.

Java programmers might consider this similar to the BeanInfo and specifically the FeatureDescriptor classes. Further down the road it may be possible to use BeanInfo classes but for now it is important that the protocol can be modified as needed.

Can this be achieved?

We can but try.

This objective has been behind the development of InterActor to provide a more generically structured basis for the required interfaces, and one where it was possible to modify underlying protocols in any generalization process.

You are toward the end of your Odyssey, and are now in step with Cut The Crap on its own Odyssey. In a while you will get a glimpse of the direction that we are travelling, and any and all feedback is appreciated.

The next immediate stage is the continuing refinement of the generated InterActor based interfaces, and refinement of GPO and MetaSpec protocols to support it.

Work will continue on the development of the spreadsheet type programming support. Integration with Alchemist generated code will increase the sophistication of the generated object models.

But for now, how far have we got?

We will revisit our Alchemist generated model.

If you locate the "contacts" directory using a file explorer, you will find a "bin" folder has been generated. Within the bin folder, you will find three files - browse.bat browse.sh and contacts.desc.

On windows you should be able to simply drag the contacts.desc file onto the browse.bat file, on unix you should first ensure that the browse.sh file is executable.

You should see a window open. On the left hand panel, at the top should be an element with a small red ball beside it, labelled "AddressBook". The "red ball" will become familiar to you, it indicates a GPO element. Click, drag and release the element over the main window area.

A new internal "Inspector" window should popup smoothly.

The Object Inspector

An Object Inspector can be created by dragging any GPO object element onto a clear part of the desktop.

References to a GPO object may be "saved" by dragging into a clipboard "slot" on the left panel.

If you click on a GPO element it will replace the current object in the local inspector.

On the left pane, are the "view" options. If the object has any "attributes", then an "attributes" option will display them. Similarly for single "associations".

If the object can provide access to any "sets" these too are available, but each "set" is listed separately. If there are more than two "sets", a "popup" will provide access to the full list.

If the object can "create" any other objects, these "creation methods" are similarly accessible.

Lastly, if any "business" methods have been specified, they too can be accessed and invoked.

Have a play. Let us know what you think about it. All ideas and suggestions are welcome.

The End?

Nope, but we have at least begun.