The Alchemist works by identifying common patterns. These are not simply low-level programming patterns, but ones more akin to overall application structure. How is a system initialized? - or - How is data created?
The main patterns are described here, for more details check out the whitepaper describing the example Northwind implementation.
The standard GPO system includes a class OMClient
that aids in system initialization. For each system, the Alchemist generates
a specialized subclass of OMClient that provides access to the
Root Object.
After system initialization the Root Object provides the top level
application interaction. For example, the Northwind system defines a Business
class, of which an instance is the Root Object. This will provide
creation and lookup methods to access other objects such as instances of Product.
Each class may define a number of associated properties - a Contact may have
a PostCode for example. In order to help with visualization, you may specify one
of these properties to label the object.
Any object property may be provided with a default value.
Some properties must be set. For example, a Contact requires a Name,
it is not sufficient to simply declare a default value.
If a property is declared as required, then the class constructor
will include the property value as a parameter.
Furthermore, generated creation methods will require the same parameters before themselves calling the constructor.
This is a general solution to property validation.
Java provides simple types and classes, and at the
language level this is sufficient: that a property is a Date a
String or an Integer. But this is not sufficient when
it comes to a system utilizing such values.
For example, a String value may be limited to some length, or to
different ranges of characters - such as a Post Code.
An Integer may be limited to a range of values, or a Date
may be used to indicate either a calendar day or to time some system event to
millisecond precision.
An approach to dealing with this is the concept of type restriction.
Here is an example of a type restriction that could be used to validate a
Post Code property:
regex:^[A-Z]{1,2}[0-9]{1,2}( [0-9][A-Z]{2})?$
...or this for a restriction on a Date property:
timeslot:am,pm
Such restrictions are declared to the Alchemist and are accessible to applications via a meta data interface in the generated code.
These are the essence of any object model. There are three standard types of
associations: one-to-one, one-to-many and
many-to-many.
How these associations are used though is decided by other patterns.
In our Northwind example an Order has a Customer
Contact and a DeliveredTo Contact, thus there are
two one-to-many associations between Order and
Contact.
This is represented by declaring association roles using an optional knownas
attribute, for example:
<assoc> <one src='Contact' knownas='Customer' required='true'/> <many src='Order'/> </assoc> <assoc> <one src='Contact' knownas='DeliveredTo'/> <many src='Order' knownas="ReceivedOrder"/> </assoc>
You might notice that associations can be required as well
as properties.
The declaration of a one-to-many or many-to-many
allow the declaration of an "owner" of the association. For example, if
a Business is the "owner" in a one-to-many association
with Products, then the Alchemist will generate methods on the
Business class to create new instances of Product
and add them to the set of Products implied by the association.
A key feature of the GPO model is the way that sets are represented.
GPO can efficiently represent and manage sets with millions of
object members, access to such sets return object derived from the java Iterator
class - from a database viewpoint, these could be likened to CURSORS.
In example of a one-to-many association between Business
and Products, a getProducts() method is generated for the
Business class.
Frequently, an application will need to access/lookup some specific member of a set
based on some property value - accessing a specific Product for example.
When declaring an association, the many end of an association may be
declared as classified and the property used for the classification
is provided, for example:
classified="Name"
would lead to the generation of a getProductByName(String name) method.
In addition to type restrictions already described, there is more information that is required
in order to generate usable user interfaces. These are guihints, and are
used to "hint" at interface requirements, for example:
<attr label='Address' type='java.lang.String' guihint='rows:3'/>
indicates that a multi-row text input box of 3 rows should be used to display and edit the property value, while:
<attr label="Price"
type='java.lang.Double'
guihint='numberformat:###,##0.00'
formula="(sum $$OrderItem.$Price)"/>
Indicates the format that should be used to display the value of the property.
Notice that there is a significant difference between guihint and
restriction, a restriction deals with the value of the
property, while the guihint deals with the display of the value.
You may have noticed in the above example the use of a formula declaration.
This is really far more than a pattern, it is a whole new idiom and you should check out the relevant whitepaper for more information.
That formulas can be declared for property values that navigate the object model
is true breakthrough functionality, and dramatically increases the number of systems that can be
fully generated.