Melasse glue framework.
Example application shows how Melasse can be used to declare UI data bindings.
- Download application,
- double-click to launch examples.
Main usage class is melasse.Binder
, which prepares UI bindings.
Binder.bind(/* source, target, options */);
Source/observed element and as target/endpoint are specified to Melasse using object paths:
import melasse.Binder;
Binder.bind("columnModel.columnCount", aJTable, // observed
"columnCount", status, // target
optionMap);
In previous example, starting from aJTable
(not null instance of JTable
) Melasse will observe change on columnCount
property of columnModel
, which is statically equivalent to:
aJTable.getColumnModel().getColumnCount();
Using its own object path API allows Melasse to observe 'deep' property, which are not directly on the root (there aJTable
), even if at some point element of the path is null when binding is set up.
In previous example, even if property columnModel
of aJTable
is null when binding is created, as soon as it will be set Melasse will start observing columnCount
on this newly set property.
For some type, it's possible have a one-to-many binding, e.g. Enable action only if all related bindings are evaluated to true.
import melasse.Binder;
Binder.bind("prop1", sourceRoot,
"enabled[]", anAction,
optionMap);
Binder.bind("prop2", sourceRoot,
"enabled[]", anAction,
optionMap);
// anAction is enabled only if both bindings are true
Mediator factory can be configured either on source or target path, in order to wrap referenced object so that property can be observed or set.
BindingOptionMap opts = new BindingOptionMap().
add(BindingKey.INPUT_MEDIATOR_FACTORY, factoryToWrapSource).
add(BindingKey.OUTPUT_MEDIATOR_FACTORY, factoryToWrapTarget);
- AppliedTransformer
- IntegerToBooleanTransformer: Returns false if null or 0.
- NegateBooleanTransformer
- NotNullTransformer
import melasse.Binder;
Binder.bind("size", anyAWTComponent,
"targetProp", target,
optionMap);
UI properties transformed as boolean can be bound to activation of button or action using enabled
property:
import melasse.Binder;
Binder.bind("sourcePath", sourceRoot,
"enabled", anAction,
optionMap);
import melasse.Binder;
Binder.bind("text", textComponent,
"targetPath", targetRoot,
optionMap);
- If
TextBindingKey.CONTINUOUSLY_UPDATE_VALUE
is set: listens to each key event. - If
TextBindingKey.CONTINUOUSLY_UPDATE_VALUE
is not set: listens to action event and focus lost event.
If text component is editable, programmatical call to
setText
doesn't trigger binding, only changes from UI do. If component is not editable property change will be bound.
Most interesting transformers for text components are:
- StringLengthToBooleanTransformer
- StringToCharArrayTransformer
Enable button/action only if value is provided to a text component:
import melasse.StringLengthToBooleanTransformer;
import melasse.BindingOptionMap;
import melasse.TextBindingKey;
import melasse.Binder;
Binder.bind("text", textComponent,
"enabled", anAction,
new BindingOptionMap().
add(BindingKey.INPUT_TRANSFORMER,
StringLengthToBooleanTransformer.
getTrimmingInstance()).
add(TextBindingKey.CONTINUOUSLY_UPDATE_VALUE));
Numeric properties can be bound, from model or from component, with specific options:
import melasse.NumericBindingKey;
import melasse.BindingOptionMap;
BindingOptionMap opts1 = new BindingOptionMap().
add(NumericBindingKey.BIGINTEGER_TO_INTEGER);
BindingOptionMap opts2 = new BindingOptionMap().
add(NumericBindingKey.INTEGER_TO_BIGINTEGER);
Number can also be bound to string using a format:
import melasse.NumberToStringTransformer;
Binder.bind("numberProp", source, "text", aLabel,
new BindingOptionMap().add(BindingKey.INPUT_TRANSFORMER,
NumberToStringTransformer.
getInstance(aJavaTextFormat)));
Visibility of error can be directly bound using JLabel.visible
as endpoint.
JLabel errorDisplay = new JLabel("Missing value");
JTextField field = new JTextField();
// ... lay out comonents
Binder.bind("visible", errorDisplay, "text", field,
new BindingOptionMap().
add(BindingKey.INPUT_TRANSFORMER,
StringLengthToBooleanTransformer.
getTrimmingInstance().negate()).
add(TextBindingKey.CONTINUOUSLY_UPDATE_VALUE));
// |errorDisplay| will be visible when |field| is empty
Melasse provide a specific support to bind following list selection properties: selectionEmpty
, minSelectionIndex
(first selected index, or -1) and maxSelectionIndex
.
import melasse.NegateBooleanTransformer;
Binder.bind("selectionEmpty", aSelectionModel, "enabled", anAction,
new BindingOptionMap().add(BindingKey.INPUT_TRANSFORMER,
NegateBooleanTransformer.getInstance()));
As Melasse can be bound to property change on Java Bean, it also provided an enhanced utility to fire such changes.
This utility allow to declare dependencies between properties, so that if property B is depending on A, when A is changed there are 2 change events which are fired, first for A and second for B.
This is also useful to easily generate change event based on several properties. As dependent property can be a computed one, result of computation is fired as change event each time properties it depends on are changed.
Way bindings are set up can be configured providing options, as firth argument of the bind
method.
- targetModeOptions: When using a read-only property as binding source. Avoid warning such as
Target object does not support setting value for property X: myInstance@123456
.
melasse.swing.TableModel
: Change support for propertiesrowCount
,columnCount
,dataVector
, using new method.willChange()
.
- melasse.QuietWrapAction
Melasse can be used in Maven or SBT project using Applicius repository.
For Maven project:
<project>
<!-- ... -->
<repositories>
<!-- ... -->
<repository>
<id>applicius-releases</id>
<name>Applicius Maven2 Releases Repository</name>
<url>https://raw.github.com/applicius/mvn-repo/master/releases/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>melasse</groupId>
<artifactId>melasse-core</artifactId>
<version>VERSION</version>
</dependency>
</dependencies>
<!-- ... -->
</project>
For SBT project:
resolvers += "Applicius Releases" at "https://raw.github.com/applicius/mvn-repo/master/releases/"
libraryDependencies += "melasse" %% "melasse-core" % "VERSION"
Melasse is built using Maven 3:
# git clone https://github.com/cchantep/melasse.git
# cd melasse
# mvn install
Pre-requisites:
- GIT client
- Maven 3+
- JDK 1.6+