The JLayer framework is an open-source implementation hosted on GitHub, its Javadoc can be found here. Here we present the technical details using an (artificial) example.
The JLayer framework includes four annotations, all of which are used in the following unit class example.
@LayerUnit
class Unit {@LayerField
Type x;@LayerField
Unit[]v
;@LayerField
Type[]w
;@LayerMethod
Typef(Type p)
{…};@LayerMethod
Typeg(
{…}; }@LayerParam
Type p)
As class Unit
is marked by the annotation @LayerUnit
,
its compilation leads to the generation of class Layer_Unit_
.
class Layer_Unit_ … { … // field itemsLayer<Unit,Type> x
;VectorLayer<Unit,Unit> v
;VectorLayer<Unit,Type> w
; … // method itemsBasedLayer<Type> f(Type p)
…;BasedLayer<Type> g(Layer<?,Type> p)
…; … }
This class contains the identifiers of all fields marked with @LayerField
and all methods marked with @LayerMethod
,
with their declarations chosen so that arrays with object elements of type Unit
can be wrapped in the desired way.
… // declaring arrays Unit a1[]; Unit a2[][]; … // the arrays have to be created and initialized … // then they can be wrappedLayer_Unit_ wa1 = new Layer_Unit_(a1);
Layer_Unit_ wa2 = new Layer_Unit_(a2);
…
At this point, the "lifted dot notation" can be used to access the so called layers.
wa1.x
, wa1.v
, wa1.w
, wa2.x
, wa2.v
, wa2.w
are called field layers
wa1.f()
, wa1.g()
, wa2.f()
, wa2.g()
are called method layers
wa1.x
, wa1.v
, wa1.w
, wa1.f()
, wa1.g()
are one-dimensional layers
wa2.x
, wa2.v
, wa2.w
, wa2.f()
, wa2.g()
are two-dimensional layers
wa1.x
and wa2.x
have element type Type
wa1.v
and wa2.v
have element type Unit[]
and are also called vector layers
wa1.w
and wa2.w
have element type Type[]
and are also called vector layers
The wrapped arrays wa1
and wa2
can somehow be identified
with the arrays a1
and a2
, respectively.
However, they are also of a layer type that we call based layer.
In contrast to a field layer, a based layer
represents not just a "slice" of an array, but an entire array.
For linking objects the JLayer framework uses mathematical relations over indices to specify the desired topologies. These relations must correspond to the following interface.
interface Relation
{
boolean contains(int[] ix, int[] iy);
}
To collect the chosen references, vector fields are used.
More precisely, for the actual linking process the
generic Type VectorLayer<U,T>
,
provides two methods.
interfaceVectorLayer<U,T>
… { // connecting layersvoid connect(Layer<?,T> layer, Relation rel)
; // associating vector layersvoid associate(VectorLayer<?,T> layer, Relation rel)
; }
Three examples are used to illustrate how these methods work.
In all examples, rel
is an arbitrary relation.
Connecting vector layers with based layers
wa1.v.connect(wa2, rel);
After executing connect()
, the following sentence applies:
a1[i].v
contains a reference to object a2[j][k]
([i],[j][k])
belongs to relation rel
.
Connecting vector layers with field layers
wa1.w.connect(wa2.x, rel);
Here, the following applies after the execution:
a1[i].w
contains a reference to object a2[j][k].x
([i],[j][k])
belongs to relation rel
.
Associating vector layers with each other
wa1.w.associate(wa2.w, rel);
Linking the two reference vectors here means linking them by an object
of type Type
, which is being created on this occasion,
so that after the execution of associate()
the following applies:
a1[i].w
and a2[j][k].w
contain
a reference to the same object of type T
([i],[j][k])
belongs to relation rel
.
To illustrate the invocation of method layers, we have to differentiate between two cases,
which depend on whether the underlying annotated method
has a parameter annotated itself - with @LayerParam
- or not.
In the example given above these cases are represented by methods
f()
or g()
, respectively.
Parameters without annotation
Method f()
represents the case that the method layer
takes over the parameter list of the underlying method as it is.
However, if a method like f()
, returns a value,
the corresponding method layer returns an array (or layer) of values:
Type p0; // the parameter
p0 = …; // setting the parameter
BasedLayer<Type> ra1; // based layer for return values
ra1 = wa1.f(p0);
// invocation of the method layer
Note, that the dimensionality of the returned based layer ra1
is one, as it
corresponds to the dimensionality of the method layer wa1.f()
.
The effect of annotation @LayerParam
The method g()
has a parameter annotated with @LayerParam
.
An appropriately wrapped array (or parameter layer)
must therefore be passed to the method layer as a parameter:
Type[][] par2; // the parameter array
… // setting the parameter array
BasedLayer<Type> wpar2;
wpar2 = new LayerBase<Type>(par2); // wrapping the parameter array
…
BasedLayer<Type> ra2; // based layer for return values
ra2 = wa2.g(wpar2);
// invocation of the method layer
Here, the dimensionality of the returned based layer ra2
is two, as it
corresponds to the dimensionality of the method layer wa2.g()
.
Parallel implementation
The annotation @LayerMethod
is equipped with a parameter
runtimeMode
being of enum type RuntimeMode
:
public enum RuntimeMode { LOOP, FORKJOIN }
The default value is RuntimeMode.LOOP
and causes the method layer to be executed in a loop.
If you want to have the parallel implementation,
you have to set the runtimeMode
accordingly:
@LayerMethod(runtimeMode=RuntimeMode.FORKJOIN)
Typef(Type p)
{…};
Now the methods are executed in parallel, where the implementation is based on the Java Fork/Join Framework, which provides tools to help speed up parallel processing by attempting to use all available processor cores. Essentially, the standard loop is broken into a number of "partial loops" according to the number of available cores.
@LayerUnit
are
ignored by the JLayer annotation processor.
connect()
and associate()
.
@LayerField
annotation has an
isIndex
parameter of type boolean
,
with a default value of false
.
If the value is true
and
the field item is of type int[]
,
then the corresponding field layers are automatically indexed,
when wrapping an appropiate object array.