Jura Home | John Winn, December 2003 |
As discussed previously, Jura is a data
language and there is no difference between source code and data.
However, Jura goes one step further - there is no difference
between the jura text file that represents an object instance and the
code required to create that object programmatically, except
that the keyword new
is used to indicate that
an object is being created.
Consider again the Contact
example. This automatically-created Jura text file has been created by saving a particular instance of the Contact
class:
Contact{ name={first="John" last="Winn"} Telephone{ type=MOBILE number="+44 1234 567890" } Address:home{ street="my street address" town="Cambridge" country="UK" } }
Now let us compare the Jura and Java code for creating an identical
instance of the Contact
class
programmatically.
Jura | Java |
---|---|
new Contact{ name={first="John" last="Winn"} Telephone{ type=MOBILE number="+44 1234 567890" } Address:home{ street="my street address" town="Cambridge" country="UK" } } |
Contact c = new Contact(); c.getName().setFirst("John"); c.getName().setLast("Winn"); Telephone t = new Telephone(); t.setType(Telephone.MOBILE); t.setNumber("+44 1234 567890"); c.add(t); Address a = new Address(); a.setName("home"); a.setStreet("my street address"); a.setTown("Cambridge"); a.setCountry("UK"); c.add(a); |
The Jura code looks exactly the same as the text file
above except for the addition of the new
keyword. Apart from being far more readable than the Java
equivalent on the right, this feature of Jura means that saved objects
can be used as the basis for creating code to generate similar objects
dynamically. This ability is the key to creating Programmatic Design Patterns, generic classes and
transforms.
The elegance of representing structured data in Jura is achieved by the use of structure operators. These include the open and create operators. The open operator looks like this:
object={ {* Nested expressions *} }
The nested expressions are resolved as if they were written in a method of
the class of object
, except that the access permissions of the
current class apply. Typically, this will mean that only public properties
or methods of object
can be accessed. To allow access
to variables outside of the open operator, the popup operator $ is
provided. Here is an example of its use, with the equivalent Java code for
comparison:
Jura | Java |
---|---|
Local:name{type=String}="MyObject" object={ name = $name } |
String name = "MyObject"; object.setName(name); |
Here, a local variable called name
is declared. The name
property of object is then set to have the value of the local variable. The
popup operator is necessary to distinguish between the property of the opened
variable and the local variable, both of which are called name
.
The create operator has this syntax:
new Type{ {* Nested expressions *} }
and is equivalent to applying an open operator on a new instance of the type and inserting initialisation and validation of the instance:
(new Type())={ initialise() {* Nested expressions *} validate() }
It follows that Jura does not use constructors (although Java constructors
can be called if necessary). Instead, a parameter-less initialise()
method can be implemented to initialise an instance (e.g. perform memory
allocation), then a number of expressions executed (e.g. setting properties,
running public methods) and finally the state of the instance can be validated
by providing a validate()
method. These methods will only be
called if the instance implements the Initialisable
and Validatable
interfaces, which are examples of abilities.
Omitting new
from a create expression, is equivalent to applying
the popup operator to that expression.
We can now write a parameterised method to return a Contact
instance, given
a first name, last name and mobile number. The code looks like a template with parameters
marked with the pop-up operator $. As a reminder, this operator means 'evaluate the following expression as though
outside the create statement'.
Method:createContact{returns=Contact Par:first{type=String} Par:last{type=String} Par:mobile{type=String} // Return{ new Contact{ name={first=$first last=$last} Telephone{ type=MOBILE number=$mobile } } } }