>) e.createRandom(5);
'''Classes are RecordTypes'''
class Foo {
public int x, y, z;
}
Is a binding to the following Datatype
type Foo = { x : Integer, y : Integer, z : Integer }
'''There are three types of classes supported, and therefore three ways how objects are constructed.'''
If you create binding for your class with Bindings#getBinding( clazz ), the class must adhere one of these format. You may have to add annotations such as @Recursive, @Optional, @Arguments.
''Record-like classes:''
class Foo {
public String name;
public Object value;
}
''Immutable classes:''
class Foo {
private String name;
private Object value;
public Foo(String name, Object value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
}
''Bean-like classes:''
class Foo {
private String name;
private Object value;
public void setName(String name) {
this.name = name;
}
public void setValue(Object value) {
this.value = value;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
}
'''Static and transient fields are omited:'''
static final long serialVersionUID = -3387516993124229943L;
transient int hashCode;
'''Enumerations are Union Types'''
enum Cars { Ferrari, Porche, Lamborghini, Jaguar }
is interpreted as union type
type Cars = | Ferrari | Porche | Lamborghini | Jaguar
If you cannot modify the class, you have to create binding for it by subclassing base binding classes, eg. RecordBinding.
'''Other exceptions:'''
*java.lang.Object
is Variant.
*java.lang.Set
is Map(T, {}).
*java.lang.TreeSet
is Map(T, {}).
*java.lang.HashSet
is Map(T, {}). (Note HashSet binding has very low performance.)
*java.lang.Map
is Map(K, V).
*java.lang.TreeMap
is Map(K, V).
*java.lang.HashMap
is Map(K, V). (Note HashMap binding has very low performance.)
*java.lang.List
is Array(T).
*java.lang.ArrayList
is Array(T).
*java.lang.LinkedList
is Array(T).
*void
is {}.
*The stacktrace of Exception.class
is omited.
===Annotations===
Java Classes / Fields can be annotated with the following annotations ('''[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/annotations/|org.simantics.databoard.annotations]]''').
'''UnionTypes are abstract classes or interfaces with @Union
annotation.'''
@Union({A.class, B.class}) interface Union1 {
}
class A implements Union1 {
public int value;
}
class B implements Union1 {
public String name;
}
'''@Referable
denotes that the class has recursion and is a referable record.'''
public @Referable class Node {
public Node[] children;
}
'''Fields that can have null value have @Optional
annotation.'''
@Optional String name;
'''String valid values are set with @Pattern
as regular expression. ([http://en.wikipedia.org/wiki/Regular_expression])'''
String @Pattern("(19|20)\\d\\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])") date;
type Date = String( Pattern = "(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])" )
'''String content type is set with a @MIMEType
. ([http://en.wikipedia.org/wiki/Mime_type MIME Type])'''
@MIMEType("text/xml") String document;
'''Array size restricted with @Length.'''
@Length("[0..10]") int[] array;
@Length({"[320]", "[240]"}) int[][] image;
'''Valid numeric range is set with @Range.'''
@Range("[0..100]") double alpha;
@Range("[0..]" double length;
'''Range and Length notation:'''
*Exact Value "0"
*Exclude all "()"
*Unlimited "[..]"
*Inclusive range "[0..100]"
*Exclusive range "(0..100)"
*Inclusive lower bound and exclusive upper bound "[0..100)"
'''Engineering unit type is given with @Unit.'''
@Unit("km/h") double maxVelocity;
'''The serializer generated with reflection can be overriden with @SpecializedSerializer'''
@SpecializedSerializer(MySerializer.class)
public class MyRecord {
...
}
== Mapping Scheme ==
A ''binding scheme'' associates some data types with a unique binding. The mapping of types to bindings is bijective, there is one binding for each type and vice-versa.
DefaultBindingScheme
is a scheme that converts any datatype to a binding. It prefers java.lang.X primitives.
The Class mapping for each type is listed below.
{| style="background-color: #e9e9e9; border: 1px solid #aaaaaa; "
| '''Type'''
| '''Class'''
|- style="background-color: #f9f9f9; " |
| BooleanType
| Boolean.class
|- style="background-color: #f9f9f9; " |
| ByteType
| Byte.class
|- style="background-color: #f9f9f9; " |
| FloatType
| Float.class
|- style="background-color: #f9f9f9; " |
| DoubleType
| eDouble.class
|- style="background-color: #f9f9f9; " |
| IntegerType
| Integer.class
|- style="background-color: #f9f9f9; " |
| LongType
| Long.class
|- style="background-color: #f9f9f9; " |
| StringType
| String.class
|- style="background-color: #f9f9f9; " |
| UnionType
| TaggedObject.class
|- style="background-color: #f9f9f9; " |
| OptionType
| ValueContainer.class
|- style="background-color: #f9f9f9; " |
| RecordType
| Object[].class
|- style="background-color: #f9f9f9; " |
| ArrayType
| ArrayList.class
|- style="background-color: #f9f9f9; " |
| Array(Byte)
| byte[].class
|- style="background-color: #f9f9f9; " |
| MapType
| TreeMap.class
|- style="background-color: #f9f9f9; " |
| VariantType
| Variant.class
|}
MutableBindingScheme
is a scheme that provides a fully implementing mutable binding for all data types.
The Class mapping for each type is listed below.
{| style="background-color: #e9e9e9; border: 1px solid #aaaaaa; "
| '''Type'''
| '''Class'''
|- style="background-color: #f9f9f9; " |
| BooleanType
| MutableBoolean.class
|- style="background-color: #f9f9f9; " |
| ByteType
| MutableByte.class
|- style="background-color: #f9f9f9; " |
| FloatType
| MutableFloat.class
|- style="background-color: #f9f9f9; " |
| DoubleType
| MutableDouble.class
|- style="background-color: #f9f9f9; " |
| IntegerType
| MutableInt.class
|- style="background-color: #f9f9f9; " |
| LongType
| MutableLong.class
|- style="background-color: #f9f9f9; " |
| StringType
| MutableString.class
|- style="background-color: #f9f9f9; " |
| UnionType
| TaggedObject.class
|- style="background-color: #f9f9f9; " |
| OptionType
| ValueContainer.class
|- style="background-color: #f9f9f9; " |
| RecordType
| Object[].class
|- style="background-color: #f9f9f9; " |
| ArrayType
| ArrayList.class
|- style="background-color: #f9f9f9; " |
| MapType
| TreeMap.class
|- style="background-color: #f9f9f9; " |
| VariantType
| Variant.class
|}
===Serialization===
[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/serialization/binary/Serializer.java|Serializer.java]] is a class that serializes Values into and from binary serialization format. It follows the Databoard [[Databoard_Specification#Binary_File_Format|Binary File Format]].
Binding binding = Bindings.DOUBLE;
Serializer serializer = Bindings.getSerializer( binding );
byte[] data = serializer.serialize( new Double( 100.0 ) );
Double value = (Double) serializer.deserialize( data );
Files can be partially accessed using BinaryAccessor, see [[#Accessors|Accessors]]. This is useful when handling larger than memory files.
===Validation===
'''Value''' can be ''well-formed'' or ''valid''.
The domain of valid values are defined with restrictions in data types, and @Length
, @Range
, @Pattern
and @MIMEType
Annotations in Classes
Validation mechanism in Binding asserts that the instance is a valid value of the respective Data Type.
try {
Binding.assertInstaceIsValid( object );
} catch( BindingException e ) {
// In-valid object
}
===Other Notes===
*Binding is a Comparator, all data values are comparable, the order is defined in [[Databoard_Specification#Comparison|Specification]].
*Binding#createDefault() creates a valid instance of the Datatype.
*Binding#createRandom(int) creates a valid instance with random values. Useful for unit tests.
*Binding#clone(Object) creates a new instance with same content.
*Binding#readFrom(Object, Binding, Binding) copies contents from another object of same type.
===Parsing & Printing===
Data values are printed and parsed of the [[Databoard_Specification|Text notation]] with the following Binding
methods:
String text = binding.printValue( value, true );
Object value = binding.parseValue( text );
And also to value definitions ''name : type = value''
StringBuilder sb = new StringBuilder();
DataValueRepository repo = new DataValueRepository();
repo.put( "temp", binding, value );
binding.printValue( value, sb, repo, true );
String text = sb.toString();
Object value = binding.parseValueDefinition( text );
=Adapter=
There can be different Java Class Bindings for a single data type. For example, Double
type can be have bindings DoubleJavaBinding
and MutableDoubleBinding
to two respective classes java.lang.Double
and MutableDouble
. Instance of one binding can be adapted to instance of another with an [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/adapter/Adapter.java|Adapter]]
.
Adapter can be created automatically or implemented self.
Adapter adapter = new Adapter() { ... };
Adapter adapter = Bindings.getAdapter( domainBinding, rangeBinding );
Example:
Adapter adapter = Bindings.getAdapter(Bindings.MUTABLE_DOUBLE, Bindings.DOUBLE);
java.lang.Double double = adapter.adapt( new MutableDouble(5.0) );
There is also convenience.
java.lang.Double double = Bindings.adapt( new MutableDouble(5.0), Bindings.MUTABLE_DOUBLE, Bindings.DOUBLE );
The argument given to Adapter#adapt(Object)
may be re-used in the result unless the adapter is a cloning adapter which guarantees a clone. Note, even wih cloning adapters immutable classes, (eg java.lang.Integer) are never cloned.
Adapter cloner = Bindings.adapterCache.getAdapter(domain, range, false, true);
cloner.adapt( ... );
Rectangle2D rect2 = Bindings.clone( rect1, rectBinding, rectBinding );
===Type Conversion===
In some cases different types may be are type-conversion compatible. An instance of one type is convertible to instance of another.
'''Engineering Units of same quantity are convertible.'''
class CarSI {
String modelName;
@Unit("km/h") double maxVelocity;
@Unit("kg") double mass;
@Unit("cm") double length;
@Unit("kW") double power;
}
class CarIm {
String modelName;
@Unit("mph") float maxVelocity;
@Unit("lbs") float mass;
@Unit("ft") float length;
@Unit("hp(M)") float power;
}
Adapter si2imAdapter = Bindings.getTypeAdapter(
Bindings.getBinding(CarSI.class),
Bindings.getBinding(CarIm.class) );
CarIm americanCarInfo = si2imAdapter.adapt( europeanCarInfo );
'''Primitive Types.''' Note, primitive adapter throws an exception at runtime if values are not adaptable.
Adapter adapter = getTypeAdapter( integerBinding, doubleBinding );
Double double = adapter.adapt( new Integer( 5 ) );
'''Records are matched by field names.'''
class Foo {
int x, y, z;
}
class Bar {
int z, y, x;
}
Adapter adapter = getTypeAdapter( fooBinding, barBinding );
'''Subtype to supertype:''' Note, this conversion cannot be not symmetric, supertypes cannot be converted to subtypes.
class Node {
String id;
}
class ValueNode extends Node {
Object value;
}
Adapter adapter = getTypeAdapter( valueNodeBinding, nodeBinding );
'''Non-existing fields to Optional fields'''
class Node {
String id;
}
class NominalNode {
String id;
@Optional String name;
}
Adapter adapter = getTypeAdapter( nodeBinding, nominalNodeBinding );
'''Enumerations'''
enum Cars { Audio, BMW, Mercedes, Honda, Mazda, Toyota, Ford, Mitsubishi, Nissan, GM }
enum JapaneseCars { Honda, Mazda, Toyota, Nissan, Mitsubishi }
Binding carsBinding = Bindings.getBinding( Cars.class );
Binding japaneseCarsBinding = Bindings.getBinding( JapaneseCars.class );
Adapter adapter = Bindings.adapterCache.getAdapter(japaneseCarsBinding, carsBinding, true, false);
=Accessors=
Say, you have several gigabytes of data in a file. The whole object doesn't need to be serialized at once. You can read and write the value partially using [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/|Accessor]] interface. The actual container can be a file, memory byte[]/ByteBuffer or a Java Object. The content is structured as tree using Databoard's type system. All but referable records are supported (=no recursion in accessors).
'''[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor|org.simantics.databoard.accessor]] interfaces'''.
{| style="background-color: #e9e9e9; border: 1px solid #aaaaaa; "
| '''Class'''
| '''Description'''
|- style="background-color: #f9f9f9; " |
| [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/Accessor.java|Accessor]]
| Base class for all data Accessors
|- style="background-color: #f9f9f9; " |
| [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/RecordAccessor.java|RecordAccessor]]
| Record
|- style="background-color: #f9f9f9; " |
| [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/ArrayAccessor.java|ArrayAccessor]]
| Array - an ordered sequence of elements of one value.
|- style="background-color: #f9f9f9; " |
| [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/MapAccessor.java|MapAccessor]]
| Map - an ''ordered'' map of keys to values.
|- style="background-color: #f9f9f9; " |
| [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/UnionAccessor.java|UnionAccessor]]
| Union
|- style="background-color: #f9f9f9; " |
| [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/BooleanAccessor.java|BooleanAccessor]],[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/IntAccessor.java|IntAccessor]],[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/LongAccessor.java|LongAccessor]],[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/FloatAccessor.java|FloatAccessor]],[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/DoubleAccessor.java|DoubleAccessor]]
| Primitive and numeric Accessors
|- style="background-color: #f9f9f9; " |
| [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/StringAccessor.java|StringAccessor]]
| String
|- style="background-color: #f9f9f9; " |
| [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/OptionalAccessor.java|OptionalAccessor]]
| Optional value
|- style="background-color: #f9f9f9; " |
| [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/VariantAccessor.java|VariantAccessor]]
| Variant value
|}
[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/Accessors.java|Accessors]] and [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/Files.java|Files]] are facade classes that contains utilities for instantiating and handling Accessors.
[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/|Binary Accessor]]
is an access to a value in binary format (byte[] or ByteBuffer).
'''Example:''' Binary accessor
Datatype type = Datatypes.getDatatype( Rectangle2D.Double.class );
Binding binding = Bindings.getBinding( Rectangle2D.Double.class );
Serializer s = Binding.getSerializer( binding );
// Serialize rectangle
Rectangle2D rect = new Rectangle2D.Double(0,0, 10, 10);
byte[] data = s.serialize(rect);
// Open accessor to byte data and modify first field in the byte data
RecordAccessor ra = Accessors.getAccessor(data, type);
ra.setFieldValue(0, Bindings.DOUBLE, 5.0);
// Deserialize values from the byte data back to the rectangle object
s.deserialize(data, rect);
System.out.println(rect.getX());
'''Example:''' File accessor, create
RecordType type = Datatypes.getDatatype( Rectangle2D.Double.class );
// Create a new file and initialize it with rectangle type, and open file accessor
FileRecordAccessor fa = Accessors.createFile( file, type );
// Write the first field (x)
fa.setFieldValue(0, Bindings.DOUBLE, 5.0);
fa.close();
'''Example:''' File accessor, open
// Open an accessor to an existing binary file
FileVariantAccessor fa = Accessors.openAccessor(file);
RecordAccessor ra = fa.getContentAccessor();
// Read the first field (x)
Double x = (Double) ra.getFieldValue(0, Bindings.DOUBLE);
fa.close();
'''Example:''' Java Accessor
Binding binding = Bindings.getBinding(Rectangle2D.Double.class);
Rectangle2D rect = new Rectangle2D.Double(0,0, 10, 10);
// Open accessor to rectangle
RecordAccessor ra = Accessors.getAccessor(binding, rect);
// Set rectangle's first field (x) to 5.0
ra.setFieldValue(0, Bindings.DOUBLE, 5.0);
System.out.println( rect.getX() );
==Accessor Reference==
Accessors can be opened to a sub-nodes with AccessorReference or by calling getAccessor. AccessorReference is a string of instances, either accessor type specific of LabelReferences.
ChildReference ref = ChildReference.compile(
new NameReference("node"),
new ComponentReference()
);
Accessor child = accessor.getComponent( ref );
ChildReference ref = ChildReference.compile(
new LabelReference("node"),
new LabelReference("v")
);
Accessor child = accessor.getComponent( ref );
ChildReference ref = ChildReference.create("n-node/v");
Accessor child = accessor.getComponent( ref );
ChildReference ref = ChildReference.create("node/v");
Accessor child = accessor.getComponent( ref );
VariantAccessor va = recordAccessor.getFieldAccessor("node");
Accessor child = va.getValueAccessor();
==Listening mechanism==
Accessor offers a monitoring mechanism for the data model.
There is an [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/interestset/InterestSet.java|InterestSet]]
that is a description of a sub-tree that is to be monitored of the data model.
[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/event/Event.java|Events]]
are objects that spawned on changes to the data model. Each event object is annotated with [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/accessor/reference/|reference path]] that is in relation to the node where the listener was placed.
Accessor Listeners use [[EventThread Pattern]] pattern.
=Utilities=
*[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/Datatypes.java|Datatypes]] is a facade class that has functions for handling Datatypes.
*[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/Bindings.java|Bindings]] is a facade class that has functions for handling Bindings.
*[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/Accessors.java|Accessors]] is a facade class that has functions for handling Accessors.
*[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/Files.java|Files]] has Read, Write and accessor functions.
*[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/Units.java|Units]] is a facade class that has functions for handling Engineering Units.
*[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/Methods.java|Methods]] has Methods, Interfaces and RPC utility functions.
*[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/util/binary/RandomAccessBinary.java|RandomAccessBinary]] is a interface for byte handling operations. In addition to basic primitive reading & writing, there are methods for grow, shrink, insert and remove.
**[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/util/binary/BinaryFile.java|BinaryFile]] and [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/util/binary/BinaryMemory.java|BinaryMemory]] are corresponding file and memory implementations.
**[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/util/binary/Blob.java|Blob]] is an implementation that represents a sub-region of a RandomAccessBinary.
==Interface Types==
There are interfaces, method types and method type definitions.
Interface type describes a software interface. It is a collection of methods type definitions.
Method type is an unnamed function with the following properties : Response Type, Request Type and ErrorType; where Response Type is any Data Type, Request Type is a Record and Error Type is an Union.
Method type definition is nominal method description.
The respective Java classes are:
*[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/method/Interface.java|Interface.java]]
*[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/method/MethodTypeDefinition.java|MethodTypeDefinition.java]]
*[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/method/MethodType.java|MethodType.java]]
In java InterfaceType description can be created with one of the following methods:
* Implementing InterfaceType
* Reading an Java Interface Class using reflection
Interface it = new Interface( ... methodDefinitions );
Interface it = getInterface( MyInterface.class );
[[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/method/MethodInterface.java|MethodInterface.java]] is a binding of an
Java Instance and an Interface Type. It decouples the method invocation from the object.
MethodInterface can be created with the following methods:
* Implementation
* Reflection
MethodInterface mi = new MethodInterface() {...}
MethodInterface mi = Datatypes.bindInterface( MyInterface.class, myObject );
Utilities Datatypes.createProxy()
and Datatypes.bindInterface()
adapt between MethodInterface and Java Instance.
MethodInterface mi = Datatypes.bindInterface( MyInterface.class, myObject );
MyInterface myObject = Datatypes.createProxy( MyInterface.class, mi );
==Remote Procedure Call==
Utilities [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/method/Server.java|Server.java]] and [[svn:foundation/databoard/trunk/org.simantics.databoard/src/org/simantics/databoard/method/Client.java|Client.java]] put MethodInterface over TCP Socket.
Server myServer = new Server(8192, mi);
MethodInterface mi = new Client("localhost", 8192);
MethodInterface with Server and Client together forms a Remote Procedure Call (RPC) mechanism.
public interface MyInterface { String helloWorld(String msg); }
[Server]
MethodInterface mi = Methods.bindInterface( MyInterface.class, myObject );
Server myServer = new Server(8192, mi);
[Client]
MethodInterface mi = new Client("localhost", 8192);
MyInterface myObject = Methods.createProxy( MyInterface.class, mi );
[[Category: Data management & Experiment Control]]