Extending Builders: Layout Builders
Tom Eugelink
tbee at tbee.org
Sun Nov 25 00:09:30 PST 2012
I've pondered a bit on the "glue". Suppose there is a plugin interface like this:
interface FxmlPlugin
{
/**
* Called after the FXML is parsed and before deserializing.
* Allows for the XML tree to be modified with, for example, XSLT.
*/
boolean beforeDocument(xml.Document document);
/**
* Called before each XML node is processed.
* JavafxNodes contains the hierarchical list of nodes with the immediate parent at (0) to the root and the end of the list.
* @return true if the engine should continue deserializing with this node, false if the node should be skipped
*/
boolean beforeNode(xml.Document document, xml.Node xmlNode, List<javafx.Node> javafxNodes);
/**
* Called after each XML node is processed.
* JavafxNodes contains the hierarchical list of nodes with the just created node at (0) to the root and the end of the list.
*/
void afterNode(xml.Document document, xml.Node xmlNode, List<javafx.Node> javafxNodes);
/**
* Called after the FXML is parsed and deserialized.
* Allows for the JavaFX nodes to be post processed
*/
void afterDocument(xml.Document document, javafx.Node javafxRoot);
}
There is a lot more to the interface than this, but adding that would obfuscate the essence at this point. In order to allow the (by me) favored FXML notation:
<HBox>
<Label fx:id="arrow" alignment="center" text="">
<HBox.C vgrow="ALWAYS" valignment="LEFT" maxWidth="Infinity"/>
<Label>
</HBox>
The plugin would look something like this (in pseudo code)
class HBoxFxmlPlugin extends FxnmlPluginImpl
{
@Override
boolean beforeNode(xml.Document document, xml.Node xmlNode, List<javafx.Node> javafxNodes)
{
// when processing HBox.C
if (xmlNode instanceof HBox.C == false) return true; // maybe we should create a annotation for this
// parent of parent must be HBox
if (javafxNodes.get(1) instanceof HBox == false) throw exception;
// create constraint
HBox.C c = new HBox.C();
if (xmlNode.getAttribute("vgrow") != null) c.vgrow( xmlNode.getAttribute("vgrow") );
if (xmlNode.getAttribute("valignment") != null) c.valignment( xmlNode.getAttribute("valignment") );
...
// set constraint for node
((HBox)javafxNodes.get(1)).setConstraint( javafxNodes.get(0), c);
// the deserialize engine should not process this node anymore
return false;
}
}
Some advanced features, like setting a constraint for nodes that are not yet part of a layout (and don't even exist at that time), like so:
<HBox>
<HBox.C for="arrow" vgrow="NEVER" valignment="RIGHT"/>
</HBox>
<HBox>
<Label fx:id="arrow" alignment="center" text="">
<HBox.C vgrow="ALWAYS" valignment="LEFT" maxWidth="Infinity"/>
<Label>
</HBox>
Can be handled by detecting the "for" attribute, temporarily storing the xml and Javafx node plus the layout, and adding the constraint in the afterDocument method.
Something similar can be done for scene builder. Basically scene builder manipulates a XML tree, so by responding to events:
interface SceneBuilderPlugin
{
void beforeNodeAdd(Document document, Node parent, Node node);
void afterNodeAdd(Document document, Node parent, Node node);
void beforeNodeRemove(Document document, Node parent, Node node);
void afterNodeRemove(Document document, Node parent, Node node);
}
The "HBox.C" node can easily be inserted after a node is added to an HBox layout. Or when changing the layout class from HBox to, say, GridPane, the scene builder first should:
- remove all children from the layout (thus also removing the HBox.C constraint class),
- then remove the HBox node,
- add a new GridPane node,
- add all children to the GridPane (thus causing the default GridPane.C to be added)
In this way the Java API can be completely clean, and all the glue is neatly placed into two clear classes, which are provided as part of the jar.
Tom
On 2012-11-24 08:18, Tom Eugelink wrote:
> But I'd rather use a different approach: provide (de)serializers that help map the FXML onto the objects. I see FXML as a layer on top of the API and good software development teachings say that lower layers should not have any knowledge of higher layers, so I feel really really bad about polluting the Java API to support FXML. Providing glue might be a better approach. Java has standard plugin-style solutions in place, like META-INF/services, so it should be easily possible to provide plugins together with the components that help in those places where simple serialization does not cut it.
>
> http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider
More information about the openjfx-dev
mailing list