Java Tutorial v1.3

 

In this document, we are going to discover the hx2a package step by step, and build a number of examples which are fully compilable. The final code of the example being developed can be found in the hx2a distribution. Every time the source compiles, it is shown in green, and when we are still working at the code and it does not compile yet, it is shown in red. The tutorial assumes that the reader is familiar with Java constructs, including class derivation, implementation of interfaces, nested classes.

Entries declaration

Attributes declaration

Relationships between classes

Crossing relationships both ways


Entries declaration

Entries are specific class fields which can trigger the recomputation of hx2a's attributes. The declaration of an hx2a entry involves the use of the Entry generic class:

import hx2a.*;

public class Tyre{

private Entry<Float> pressure;

}

The argument of the generic Entry class is the type of the entry being defined, Float here. We are not very far from a regular declaration of the same class using classical Java declarations:

public class Tyre{

private int diameter;

}

There is one constraint on the type used in entries (like Float above), they need to implement the Comparable interface. In case of a user defined type, please ensure that the type you use implements Comparable.

As expected , nothing prevents from having both hx2a entries and regular Java class fields in the same class:

import hx2a.*;

public class Tyre{

private Entry<Float> pressure;
private int diameter;

}

However, the initialization of the entry in the constructor of the bearer class is slightly different:

import hx2a.*;

public class Tyre{

public Tyre(){

pressure=new Entry<Float>(new Float(34));
diameter=16;

}
private Entry<Float> pressure;
private int diameter;

}

Attributes in the package being developed will not react to native fields updates. The attributes will be insensitive to the changes in Tyre diameter, but they will be sensitive to pressure change.

When using generics, it can become very fastidious to type many types the list of type arguments. It is always possible to derive them to define a shorter name:

import hx2a.*;

public class Tyre{

public class Pressure extends Entry<Float>{

public Pressure(float value){super(value);}

}
public Tyre(){

pressure=new Pressure(34);
diameter=16;

}

private Pressure pressure;
private int diameter;

}

After this, no need to repeat Entry<Float> everywhere, Pressure is sufficient. In addition, we have seized the opportunity to simplify the Float type into the native float one. The latter will be automatically converted in to the former. We could not have declared the entry using float directly, as Java generics require full class types, not native types.

Now, the declaration of the pressure field looks like any native Java field declaration. It is possible to construct an entry using one of two constructors, one letting the entry uninitialized, the other one setting it with a value. In our example, adding the possibility of an uninitialized pressure translates into:

import hx2a.*;

public class Tyre{

public class Pressure extends Entry<Float>{

public Pressure(){}
public Pressure(float pressure){super(pressure);}

}
public Tyre(){

pressure=new Pressure(34);
diameter=16;

}
private Pressure pressure;
private int diameter;

}

Now, the most important is to come, how do we get the value of an entry, and how do we update that value? This is very simple, the entry generic offers to methods for that, getValue and setValue:

import hx2a.*;

public class Tyre{

public class Pressure extends Entry<Float>{

public Pressure(){}
public Pressure(float pressure){super(pressure);}

}
public Tyre(){

pressure=new Pressure(34);
diameter=16;

}
Float getPressure(){return pressure.getValue();}
void setPressure(float pressure){pressure.setValue(pressure);}

private Pressure pressure;
private int diameter;

}

Nothing else needs to be done to trigger attributes recomputation.

As a last remark, nothing prevents an entry to be of a complex type, including an entire structure.

Attributes declaration

We are now going to define attributes. Attributes are different from entries, because their value cannot be updated, it is set by an evaluation function. Let us define a Car class, with four tyres, each of them having a pressure, and let us add to the Car class a pressure too, which is the average of the pressure of the four tyres:

import hx2a.*;

public class Car{

public Car(){

frontLeft=new Tyre();
frontRight=new Tyre();
rearLeft=new Tyre();
rearRight=new Tyre();


}
private float computeTyrePressure(){

return (frontLeft.getPressure()+frontRight.getPressure()+
rearLeft.getPressure()+rearRight.getPressure())/4;

}
private Tyre frontLeft;
private Tyre frontRight;
private Tyre rearLeft;
private Tyre rearRight;

}

defines the car class, and the computation of the average pressure. Now we must add an attribute which has the pressure computation as an evaluation function. Attributes are instances of a generic class too, just like entires:

import hx2a.*;

public class Car{

public Car(){

frontLeft=new Tyre();
frontRight=new Tyre();
rearLeft=new Tyre();
rearRight=new Tyre();

}
private float computeTyrePressure(){

return (frontLeft.getPressure()+frontRight.getPressure()+
rearLeft.getPressure()+rearRight.getPressure())/4;

}
private Tyre frontLeft;
private Tyre frontRight;
private Tyre rearLeft;
private Tyre rearRight;
private Attribute<Car,Float> pressure;

}

Notice that there are two arguments for the generic class Attribute. The first argument is the name of the class bearing the attribute.
But where is the link between getTyrePressure and the pressure attribute? At least, if you try the code above, it compiles well. Let us try constructing the attribute in the car constructor:

import hx2a.*;

public class Car{

public Car(){

frontLeft=new Tyre();
frontRight=new Tyre();
rearLeft=new Tyre();
rearRight=new Tyre();
pressure=new Attribute<Car,Float>(this);

}
private float computeTyrePressure(){

return (frontLeft.getPressure()+frontRight.getPressure()+
rearLeft.getPressure()+rearRight.getPressure())/4;

}
private Tyre frontLeft;
private Tyre frontRight;
private Tyre rearLeft;
private Tyre rearRight;
private Attribute<Car,Float> pressure;

}

This looks good, but if you try to compile it, you'll notice that it does not compile (it explains why it is in red). Why? Because the generic class Attribute cannot be used as simply as that. It contains the following method:

protected abstract T computeValue() throws Looping;

Where T is the type of the attribute (Float here). It means this class is abstract, and cannot be used without deriving it. Let us rewrite the Car class in the same spirit we derived Entry<Float> to define the Pressure type:

import hx2a.*;

public class Car{

public class Pressure extends Attribute<Car,Float>{

public Pressure(Car car){super(car);}
public Float computeValue() throws Looping{

return getBearer().computeTyrePressure();

}

}
public Car(){

frontLeft=new Tyre();
frontRight=new Tyre();
rearLeft=new Tyre();
rearRight=new Tyre();
pressure=new Pressure(this);

}
private float computeTyrePressure(){

return (frontLeft.getPressure()+frontRight.getPressure()+
rearLeft.getPressure()+rearRight.getPressure())/4;

}
private Tyre frontLeft;
private Tyre frontRight;
private Tyre rearLeft;
private Tyre rearRight;
private Pressure pressure;

}

After reading carefully, nothing surprising. We see that the method getBearer() has been used on the attribute to get the car object. With this object, the computeTyrePressure method is called. This way, there is now a link between the attribute and its evaluation function.

Now, how do we access the value of the attribute? Here is how:

import hx2a.*;

public class Car{

public class Pressure extends Attribute<Car,Float>{

public Pressure(Car car){super(car);}
public Float computeValue() throws Looping{

return getBearer().computeTyrePressure();

}

}
public Car(){

frontLeft=new Tyre();
frontRight=new Tyre();
rearLeft=new Tyre();
rearRight=new Tyre();
pressure=new Pressure(this);

}
public float getTyrePressure(){return pressure.getValue();}
private float computeTyrePressure(){

return (frontLeft.getPressure()+frontRight.getPressure()+
rearLeft.getPressure()+rearRight.getPressure())/4;

}
private Tyre frontLeft;
private Tyre frontRight;
private Tyre rearLeft;
private Tyre rearRight;
private Pressure pressure;

}

The color is red, so there must be some problem with this code, right? Indeed, pressure.getValue might rise an exception, of type Looping when the attribute evaluator encounters a dependency loop , so it needs to be added to the signature of the getTyrePressure method:

import hx2a.*;

public class Car{

public class Pressure extends Attribute<Car,Float>{

public Pressure(Car car){super(car);}
public Float computeValue() throws Looping{

return getBearer().computeTyrePressure();

}

}
public Car(){

frontLeft=new Tyre();
frontRight=new Tyre();
rearLeft=new Tyre();
rearRight=new Tyre();
pressure=new Pressure(this);

}
public float getTyrePressure() throws Looping {return pressure.getValue();}
private Float computeTyrePressure(){

return (frontLeft.getPressure()+frontRight.getPressure()+
rearLeft.getPressure()+rearRight.getPressure())/4;

}
private Tyre frontLeft;
private Tyre frontRight;
private Tyre rearLeft;
private Tyre rearRight;
private Pressure pressure;

}

Now that everything compiles, we can test all this now in a main:

import hx2a.*;

public class Main{

public static void main(String[] args){

Car car=new Car();
try{

System.out.println(car.getTyrePressure());

}
catch(Looping l){

System.out.println("Looping");

}

}

}

When executed, it prints 34.0, which is the correct value. Until now, there is no miracle, any Java method would do the same, without going through the pain of attributes and entries.

We are gradually going to come to a more spectacular demonstration. In order to do that, we are going to allow for individual tyre manipulation, and we are going to create the class Garage, which contains several cars. We modify the car the following way, to allow for individual tyre manipulation:

import hx2a.*;

public class Car{

public class Pressure extends Attribute<Car,Float>{

public Pressure(Car car){super(car);}
public Float computeValue() throws Looping{

return getBearer().computeTyrePressure();

}

}
public Car(Tyre fl, Tyre fr, Tyre rl, Tyre rr ){

frontLeft=fl;
frontRight=fr;
rearLeft=rl;
rearRight=rr;
pressure=new Pressure(this);

}
public Tyre getFrontLeft(){return frontLeft;}
public Tyre getFrontRight(){return frontRight;}
public Tyre getRearLeft(){return rearLeft;}
public Tyre getRearRight(){return rearRight;}

public float getTyrePressure() throws Looping {return pressure.getValue();}
private float computeTyrePressure(){

return (frontLeft.getPressure()+frontRight.getPressure()+
rearLeft.getPressure()+rearRight.getPressure())/4;

}
private Tyre frontLeft;
private Tyre frontRight;
private Tyre rearLeft;
private Tyre rearRight;
private Pressure pressure;

}

We need to update the main too:

import hx2a.*;

public class Main{

public static void main(String[] args){

Tyre frontLeft=new Tyre();
Tyre frontRight=new Tyre();
Tyre rearLeft=new Tyre();
Tyre rearRight=new Tyre();

Car car=new Car(frontLeft,frontRight,rearLeft,rearRight);
try{

System.out.println(car.getTyrePressure());

}
catch(Looping l){

System.out.println("Looping");

}

}

}

When run, it still displays 34.0, which is normal. Let us alter a little bit the main to reduce the pressure of one tyre, and check the resulting overall pressure:

import hx2a.*;

public class Main{

public static void main(String[] args){

Tyre frontLeft=new Tyre();
Tyre frontRight=new Tyre();
Tyre rearLeft=new Tyre();
Tyre rearRight=new Tyre();
Car car=new Car(frontLeft,frontRight,rearLeft,rearRight);
try{

System.out.println(car.getTyrePressure());
rearRight.setPressure(10);
System.out.println(car.getTyrePressure());

}
catch(Looping l){

System.out.println("Looping");

}

}

}

The display is now 28.0, which is the correct value. Again, nothing fancy until now. Let us add a name field in the car class and a little method in the car pressure attribute:

import hx2a.*;

public class Car{

public class Pressure extends Attribute<Car,Float>{

public Pressure(Car car){super(car);}
public Float computeValue() throws Looping{

return getBearer().computeTyrePressure();

}
protected void reassigning(Float previous, Float next){

System.out.println(
"Setting new pressure for car "+getBearer().getName()+
" from "+previous+" to "+next);

}

}
public Car(String n, Tyre fl, Tyre fr, Tyre rl, Tyre rr){

name=n;
frontLeft=fl;
frontRight=fr;
rearLeft=rl;
rearRight=rr;
pressure=new Pressure(this);

}
public String getName(){return name;}
public Tyre getFrontLeft(){return frontLeft;}
public Tyre getFrontRight(){return frontRight;}
public Tyre getRearLeft(){return rearLeft;}
public Tyre getRearRight(){return rearRight;}
public float getTyrePressure() throws Looping {return pressure.getValue();}
private float computeTyrePressure(){

return (frontLeft.getPressure()+frontRight.getPressure()+
rearLeft.getPressure()+rearRight.getPressure())/4;

}
private Tyre frontLeft;
private Tyre frontRight;
private Tyre rearLeft;
private Tyre rearRight;
private Pressure pressure;

}

The method reassigning is called just before an attribute is assigned a different value. It passes the former and newer value. We use feature to display the previous and next pressures for a car.
We need to change the main, because the car constructor takes an additional parameter, the name. Also, as the function reassigning will print all necessary information, we do not need anymore to print the pressure of the car, we need only to access it to have it computed:

import hx2a.*;

public class Main{

public static void main(String[] args){

Tyre frontLeft=new Tyre();
Tyre frontRight=new Tyre();
Tyre rearLeft=new Tyre();
Tyre rearRight=new Tyre();
Car car=new Car("Salmson",frontLeft,frontRight,rearLeft,rearRight);
try{

car.getTyrePressure();
rearRight.setPressure(10);
car.getTyrePressure();

}
catch(Looping l){

System.out.println("Looping");

}

}

}

When executed, it yields:

Setting new pressure for car Salmson from null to 34.0
Setting new pressure for car Salmson from 34.0 to 28.0

The attribute has been set a value different from its previous one twice.

Now let us do something tricky, and increase the pressure of a tyre, while decreasing the pressure of another tyre of the same amount:

import hx2a.*;

public class Main{

public static void main(String[] args){

Tyre frontLeft=new Tyre();
Tyre frontRight=new Tyre();
Tyre rearLeft=new Tyre();
Tyre rearRight=new Tyre();
Car car=new Car("Salmson",frontLeft,frontRight,rearLeft,rearRight);
try{

car.getTyrePressure();
rearRight.setPressure(10);
rearLeft.setPressure(58);
car.getTyrePressure();

}
catch(Looping l){

System.out.println("Looping");

}

}

}

When executed, it yields:

Setting new pressure for car Salmson from null to 34.0

And there is no other assignment of a value, because the change of pressure of the two tyres compensate, and the overall pressure average stays identical.

Relationships between classes

Now, what about replacing a tyre in a car? Obviously, this should affect the average pressure of the car. This is something a spreadsheet is not able to make, as the dependencies between the cell are not driven by a datastructure bearing the cells themselves. There is no "tyre" in the equivalent spreadsheet, only its pressure.
But the thing is that when changing a tyre, a native Java field of the car class is updated, and attributes are not sensitive to native fields updates. What is the solution? Make the field an entry, right? This way, the relationship between the car and the tyres will be active and the replacement of a tyre will trigger the recomputation of the average pressure of the car:

import hx2a.*;

public class Car{

public class HoldsATyre extends Entry<Tyre>{

public HoldsATyre(Tyre tyre){super(tyre);}

}
public class Pressure extends Attribute<Car,Float>{

public Pressure(Car car){super(car);}
public Float computeValue() throws Looping{

return getBearer().computeTyrePressure();

}
protected void reassigning(Float previous, Float next){

System.out.println("Setting new pressure for car "+getBearer().getName()+
" from "+previous+" to "+next);

}

}
public Car(String n, Tyre fl, Tyre fr, Tyre rl, Tyre rr){

name=n;
frontLeft=new HoldsATyre(fl);
frontRight=new HoldsATyre(fr);
rearLeft=new HoldsATyre(rl);
rearRight=new HoldsATyre(rr);
pressure=new Pressure(this);

}
public String getName(){return name;}
public Tyre getFrontLeft(){return frontLeft;}
public Tyre getFrontRight(){return frontRight;}
public Tyre getRearLeft(){return rearLeft;}
public Tyre getRearRight(){return rearRight;}
public Float getTyrePressure() throws Looping {return pressure.getValue();}
private Float computeTyrePressure(){

return (frontLeft.getPressure()+frontRight.getPressure()+
rearLeft.getPressure()+rearRight.getPressure())/4;

}
private HoldsATyre frontLeft;
private HoldsATyre frontRight;
private HoldsATyre rearLeft;
private HoldsATyre rearRight;
private Pressure pressure;

}

Unfortunately, this code does not compile. The main reason has already been explained in this tutorial here. Tyre does not implement the Comparable interface. Another reason is that the getValue for all the entries need to be used to access the tyre stored in the entry.

Let us come back to the Comparable interface implementation issue. There is no point in comparing two tyres, the idea here is to have an entry which compares the references to the tyres, not the tyres themselves. There are two ways to do that, either implement the comparable interface on Tyre, or use the SimpleEntryWithBearer generic class which does it conveniently for you, without interfering with Tyre. We need also to use the getValue method offered by that class to obtain the Tyre it is pointing to. Additionally, SimpleEntryWithBearer will offer a getBearer method which returns the object holding the entry. In turn, the bearer class must be passed as a first argument to the SimpleEntryWithBearer generic:

import hx2a.*;

public class Car{

public class HoldsATyre extends SimpleEntryWithBearer<Car,Tyre>{

public HoldsATyre(Car car, Tyre tyre){super(car,tyre);}

}
public class Pressure extends Attribute<Car,Float>{

public Pressure(Car car){super(car);}
public Float computeValue() throws Looping{

return getBearer().computeTyrePressure();

}
protected void reassigning(Float previous, Float next){

System.out.println("Setting new pressure for car "+getBearer().getName()+" from "+previous+" to "+next);

}

}
public Car(String n, Tyre fl, Tyre fr, Tyre rl, Tyre rr){

name=n;
frontLeft=new HoldsATyre(this,fl);
frontRight=new HoldsATyre(this,fr);
rearLeft=new HoldsATyre(this,rl);
rearRight=new HoldsATyre(this,rr);
pressure=new Pressure(this);

}
public String getName(){return name;}
public Tyre getFrontLeft(){return frontLeft.getValue();}
public Tyre getFrontRight(){return frontRight.getValue();}
public Tyre getRearLeft(){return rearLeft.getValue();}
public Tyre getRearRight(){return rearRight.getValue();}
public Float getTyrePressure() throws Looping {return pressure.getValue();}
private Float computeTyrePressure(){

return (frontLeft.getValue().getPressure()+
frontRight.getValue().getPressure()+
rearLeft.getValue().getPressure()+
rearRight.getValue().getPressure())/4;

}
private HoldsATyre frontLeft;
private HoldsATyre frontRight;
private HoldsATyre rearLeft;
private HoldsATyre rearRight;
private Pressure pressure;

}

Now we need to add the tyre replacement routines:

import hx2a.*;

public class Car{

public class HoldsATyre extends SimpleEntryWithBearer<Car,Tyre>{

public HoldsATyre(Car car, Tyre tyre){super(car,tyre);}

}
public class Pressure extends Attribute<Car,Float>{

public Pressure(Car car){super(car);}
public Float computeValue() throws Looping{

return getBearer().computeTyrePressure();

}
protected void reassigning(Float previous, Float next){

System.out.println(
"Setting new pressure for car "+getBearer().getName()+
" from "+previous+" to "+next);

}

}
public Car(String n, Tyre fl, Tyre fr, Tyre rl, Tyre rr){

name=n;
frontLeft=new HoldsATyre(this,fl);
frontRight=new HoldsATyre(this,fr);
rearLeft=new HoldsATyre(this,rl);
rearRight=new HoldsATyre(this,rr);
pressure=new Pressure(this);

}
public String getName(){return name;}
public Tyre getFrontLeft(){return frontLeft.getValue();}
public Tyre getFrontRight(){return frontRight.getValue();}
public Tyre getRearLeft(){return rearLeft.getValue();}
public Tyre getRearRight(){return rearRight.getValue();}
public void replaceFrontLeft(Tyre tyre){frontLeft.setValue(tyre);}
public void replaceFrontRight(Tyre tyre){frontRight.setValue(tyre);}
public void replaceRearLeft(Tyre tyre){rearLeft.setValue(tyre);}
public void replaceRearRight(Tyre tyre){rearRight.setValue(tyre);}

public Float getTyrePressure() throws Looping {return pressure.getValue();}
private Float computeTyrePressure(){

return (frontLeft.getValue().getPressure()+
frontRight.getValue().getPressure()+
rearLeft.getValue().getPressure()+
rearRight.getValue().getPressure())/4;

}
private HoldsATyre frontLeft;
private HoldsATyre frontRight;
private HoldsATyre rearLeft;
private HoldsATyre rearRight;
private Pressure pressure;

}

We can now replace a tyre in the main:

import hx2a.*;

public class Main{

public static void main(String[] args){

Tyre frontLeft=new Tyre();
Tyre frontRight=new Tyre();
Tyre rearLeft=new Tyre();
Tyre rearRight=new Tyre();
Car car=new Car("Salmson",frontLeft,frontRight,rearLeft,rearRight);
try{

car.getTyrePressure();
rearRight.setPressure(10);
rearLeft.setPressure(58);
car.getTyrePressure();
Tyre newfrontLeft=new Tyre();
newfrontLeft.setPressure(0);
car.replaceFrontLeft(newfrontLeft);
car.getTyrePressure();

}
catch(Looping l){

System.out.println("Looping");

}

}

}

Yields:

Setting new pressure for car Salmson from null to 34.0
Setting new pressure for car Salmson from 34.0 to 25.5

The average pressure of the car object has been sensitive to the replacement of the tyre, and yields the correct new value.

Up to now, there is some magic, but nothing out of the ordinary. We are now going to define a garage with a bunch of cars in it (for simplicity, two...). We are going to add an attribute computing the average pressure of the cars in the garage. Eventually, we are going to replace one tyre on the Salmson car; and show that, at no cost, the only two attributes which are reassigned are the one of the Salmson and the one of the garage. The attribute on the other is not even going to be visited, as it has not been impacted.

The class Garage is as follows:

import hx2a.*;

public class Garage {

public class HoldsACar extends SimpleEntryWithBearer<Garage,Car>{

public HoldsACar(Garage garage, Car car){

super(garage,car);

}

}
public Garage(Car fc, Car sc){

firstCar=new HoldsACar(this,fc);
secondCar=new HoldsACar(this,sc);
pressure=new Pressure(this);

}
public class Pressure extends Attribute<Garage,Float>{

public Pressure(Garage garage){super(garage);}
public Float computeValue() throws Looping {return getBearer().computeTyrePressure();}
protected void reassigning(Float previous, Float next){

System.out.println("Setting new pressure for garage from "+previous+
" to "+next);

}

}
public Car getFirstCar(){return firstCar.getValue();}
public Car getSecondCar(){return secondCar.getValue();}
public Float getTyrePressure() throws Looping {

return pressure.getValue();

}
private Float computeTyrePressure() throws Looping {

return (firstCar.getValue().getTyrePressure()+secondCar.getValue().getTyrePressure())/2;

}
private HoldsACar firstCar;
private HoldsACar secondCar;
private Pressure pressure;

}

The main is structured as follows:

import hx2a.*;

public class Main {

public static void main(String[] args) {

Tyre frontLeft=new Tyre();
Tyre frontRight=new Tyre();
Tyre rearLeft=new Tyre();
Tyre rearRight=new Tyre();
Car salmson=new Car("Salmson",frontLeft, frontRight, rearLeft,rearRight);
try{

salmson.getTyrePressure();
//
rearRight.setPressure(10);
rearLeft.setPressure(58);
salmson.getTyrePressure();
//
Tyre newfrontLeft=new Tyre();
newfrontLeft.setPressure(0);
salmson.replaceFrontLeft(newfrontLeft);
salmson.getTyrePressure();
//
Garage garage=new Garage(salmson,
new Car("Kadett",new Tyre(),new Tyre(),new Tyre(),new Tyre()));
garage.getTyrePressure();
newfrontLeft.setPressure(34);
garage.getTyrePressure();
salmson.replaceFrontLeft(new Tyre());
garage.getTyrePressure();

}
catch(Looping l){

System.out.println("Looping");

}

}

}

The output of the main, is:

Setting new pressure for car Salmson from 34.0 to 25.5
Setting new pressure for car Kadett from null to 34.0
Setting new pressure for garage from null to 29.75
Setting new pressure for car Salmson from 25.5 to 34.0
Setting new pressure for garage from 29.75 to 34.0

It means that, once computed, the pressure for the Kadett was not reassigned when the pressure of the front left tyre of the Salmson was reset to normal pressure 34, and one tyre replaced. The attributes were evaluated incrementally. The evaluator did not even visit the Kadett attributes.

Crossing Relationships both ways

It can be interesting sometimes to cross relationships both ways. For instance, if a Tyre wants to know if it is below or higher than the average pressure of all the tyres of the car, it needs to have an attribute governed by an evaluation function which goes from Tyre to Car.

This presents no difficulty. A demo of the cross reference design pattern is included in the hx2a package (xref demo). You only need to have a reverse relationship from Tyre to Car, which is consistently maintained both ways when the Car to Tyre relationship is set or broken.


Top

Entries declaration

Attributes declaration

Relationships between classes

Crossing relationships both ways

 

This content is copyrighted by Vincent Lextrait - 2006-2007. For further information vincent@lextrait.com