A data type table defines an OpenL Tablets data carrier. Using data types inside the OpenL Tablets rules is recommended, since using data types created in OpenL Tablets table from Java code is limited in the current implementation. For Java code, the preferable method is to use Java or Vocabulary type. For information on how this is done, see Data Table.
The following is an example of a data type table defining a data type called Person:
The first row is the header containing the keyword 'Datatype' followed by the name of the data type. Every row, beginning with the second one, represents one property of the data type. The first column contains property types; the second column contains corresponding property names.
Datatypes tables are being processed second after Properties Table it is done to build a domain model that is used in rules.
Datatype header format:
[Datatype <typename>] or [Datatype <typename> extends <parentTypeName>] or [Datatype <typename> <aliastype>]
There is possibility to inherit one datatype from another in OpenL Tablets. New datatype that inherits another will have access to all fields defined in parent datatype. If child datatype contains fields that defined in parent you will get warnings or errors (if field declared with different types in child and parent).
Also constructor with all fields of child datatype will contains all fields from parent fields and toString, equals and hashCode methods will use all fields form parent datatype.
At runtime, when Openl engine instance is being built, for each datatype
component java byte code is being generated, it represents simple java
bean for this datatype. This byte code is being loaded to classloader so
the object of type
Class<?>
can be accessible. Using this object through reflections new instances
are being created and fields of datatypes are being initialized (see
DatatypeOpenClass
and DatatypeOpenField
classes).
As generation of datatypes is performed on runtime and users can`t
access this classes in their code, so to the
JavaWrapperAntTask
was added a goal "generate datatypes". It adds the
possibility of generation java files and putting it to the file system.
So users can use this types in their code. But there is a limitation:
users shouldn`t create instances of datatype classes before creating an
instance of OpenL engine. This is because the static class that was on
the file system will be loaded first to the classloader. That is not
good as the engine consider to work with datatypes generated at
runtime.
Look at the next 2 examples, represented right and wrong usage of datatype classes in your code. Examples will be shown with usage of dynamic OpenL wrapper.
At first call the ant task "make wrapper generate datatypes" and you will get java files of datatypes at your file system.
public interface RulesInterface {
// define the interface of the rules engine. it will be succesfully
// compiled as we have all appropriate classes at file system
DoubleValue clientDiscount(Policy policy);
}
This example demonstrates the right usage of datatype beans. To the classloader will be loaded class generated at runtime.
EngineFactory<RulesInterface> engineFactory = new
EngineFactory<RulesInterface>(RuleEngineFactory.RULE_OPENL_NAME,
"rules/main/Tutorial_4.xls", RulesInterface.class);
// create new instance of OpenL engine
RulesInterface test = engineFactory.makeInstance();
Policy policy = null;
// create new instance of datatype class after building OpenL instance
try {
policy = (Policy)
Class.forName("org.openl.generated.beans.Policy").newInstance();
policy.setClientTier("Preferred");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(test.clientDiscount(policy));
And this one demonstrates the wrong usage of datatype beans. To the classloader will be loaded class that was compiled from the file system.
// create new instance of datatype class before building OpenL instance
Policy policy = null;
try {
policy = (Policy)
Class.forName("org.openl.generated.beans.Policy").newInstance();
policy.setClientTier("Preferred");
} catch (Exception e) {
e.printStackTrace();
}
EngineFactory<RulesInterface> engineFactory = new
EngineFactory<RulesInterface>(RuleEngineFactory.RULE_OPENL_NAME,
"rules/main/Tutorial_4.xls",
RulesInterface.class);
// create new instance of OpenL engine
RulesInterface test = engineFactory.makeInstance();
System.out.println(test.clientDiscount(policy));
After parsing, each datatype is put to compilation context, so it will
be accessible for rules during binding. Also all datatypes are placed to
IOpenClass
of whole module and will be accessible from CompiledOpenClass#getTypes
when Openl wrapper wiill be generated.
Each TableSyntaxNode
that is of type xls.datatype contains an object of datatype as its member.
Working with Datatype arrays from rules
There are 2 possibilities to work with Datatype arrays from rules:
by numeric index, starting from 0
by user defined index
In the first case by call drivers[5] you will get the 6 element of the Datatype array.
Second case is a little more complicated. First field of datatype is considered to be the user defined index. For example if we have a Datatype Driver with first String field name, we can create aData Table, initializing two instances of Driver with names: John and David. Than in rules we can call the
instance we need by drivers[David]. You can use all Java types (including primitives) and Datatypes for your indexes. When the first field of Datatype is of int type called id, to call the instance from array, wrap it with quotes: e.g. drivers[7], in this case you won`t get the 8 element in the array, but the Driver with id equals to 7.