Convert FXML to Java as part of the build


FXML is a declarative way to define you JavaFX UIs, you can edit it by hand e.g. useing e(fx)clipse, use a DSL like FXGraph or use a WYSIWYG-Tool like SceneBuilder.

At runtime a class named FXMLLoader takes the FXML and creates a SceneGraph out of it using reflection. Let’s look at a small sample.

FXML:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.Button?>

<BorderPane xmlns:fx="http://javafx.com/fxml">
  <center>
    <Button text="Hello World"></Button>
  </center>
</BorderPane>

Loading in Java:

BorderPane p = FXMLLoader.load(getClass().getClassLoader().getResource("Sample.fxml"));

Simple & easy BUT it is slow. On desktop system the slowness might not bother you but on constrainted devices like Raspberry Pi or smartphone reflection it will.

So the idea I had already since some time [Bug] is to move translation from runtime to build time so that one can simply add an instruction to the build script and FXML-Files are translated to Java-Files and compiled by the Java compiler.

While not yet complete I can now share the first version which does at least supports the most basic stuff. To build my JavaFX app which uses FXML I can now use an ant-file like this:

<project default="test">
  <path id="fxcompile">
    <filelist>
      <file name="build/classes"/>
      <file name="build-libs/org.eclipse.fx.fxml.compiler_0.9.0-SNAPSHOT.jar"/>
    </filelist>
  </path>
	
  <path id="buildpath">
    <filelist>
      <file name="build/classes"/>
      <file name="libs/org.eclipse.fx.core_0.9.0.201308281625.jar"/>
    </filelist>
  </path>
	
 <target name="test">
   <delete dir="build" />
   <mkdir dir="build/classes"/>
		
   <taskdef name="fxml-compiler" classpathref="fxcompile" classname="org.eclipse.fx.ide.fxml.compiler.ant.FXMLCompilerTask" />
		
   <javac srcdir="src" destdir="build/classes" classpathref="buildpath"></javac>
		
   <fxml-compiler sourcedir="src" destdir="build/gen-src"/>
   <javac srcdir="build/gen-src" destdir="build/classes" classpathref="buildpath"></javac>
  </target>
</project>

The important line is <fxml-compiler… which instructs the build to translate all FXML-Files found below src into .java-Files and place them into build/gen-src.

Generated Java-File:

import javafx.fxml.FXMLLoader;
import javafx.scene.layout.BorderPane;
import javafx.scene.control.Button;

import java.util.Map;
import java.net.URL;
import java.util.ResourceBundle;

import org.eclipse.fx.core.fxml.FXMLDocument;

import java.util.HashMap;

@SuppressWarnings("all")
public class Sample extends FXMLDocument<BorderPane> {
  private Map<String,Object> namespaceMap = new HashMap<>();
  public Object getController() {
    return null;
  }
	
  public BorderPane load(URL location, ResourceBundle resourceBundle) {
    BorderPane root = new BorderPane();
    {
      Button e_1 = new Button();
      e_1.setText("Hello World");
      root.setCenter(e_1);
    }
    return root;
  }
}

In the original code the only change I have to make is switch from FXMLLoader to a smarter class which first checks if there is a .class-File with the same name and create an instance of it.

ExtendedFXMLLoader loader = new ExtendedFXMLLoader();
loader.load(getClass().getClassLoader(),"Sample.fxml");

You can play with it yourself. I’ve uploaded a sample project to http://downloads.efxclipse.org/fxml-compiler.zip, give it a try on your own FXML-Files and if things fail file bugs against e(fx)clipse.

This entry was posted in e(fx)clipse. Bookmark the permalink.

48 Responses to Convert FXML to Java as part of the build

  1. Great job this is the so important feaure that was missing for FXML ! I hope that FXML will leverage MXML features.
    What about creating a maven plugin too ?

  2. Pingback: JavaFX links of the week, September 2 // JavaFX News, Demos and Insight // FX Experience

  3. Pingback: Java desktop links of the week, September 2 « Jonathan Giles

  4. Hayden Jones says:

    Is it possible to download this tool by itself as a jar file?

    One thing I don’t like about fxml is that it needs all-permission to do the binding. Does generating the java class file prevent the need of the reflection API for the binding of the fxml to the controller?

    Thanks,

    • Tom Schindl says:

      The sample project holds the tool jar. On reflection if fields and methods in your controller are public no reflection is used.

      • haydenpjones says:

        Tx, I’ll have a look.

      • Tom Schindl says:

        Please note this alpha code so not all fxml files will translate and before i forget there’s still one reflection call needed

      • haydenpjones says:

        I’ve given it a try…

        I am trying to run the FXGraphCompiler directly but get an

        org.xml.sax.SAXException: javafx.geometry.Insets.getId();
        java.lang.NoSuchMethodException…

        Looking forward to see the progress you make on this project.

      • Tom Schindl says:

        Ok so your inset has an id attribute? Can you show me the part of the fxml where the inset is defined?

      • Hayden Jones says:

        HiTom,

        I deleted the id on the Insets in the fxml.

        The next problem I got was having a combobox which had three default items which the parse could not handle. I tried removing the fxcollections underneath but then I got another error which said something about parameter count.

        Maybe I can send you the fxml?

      • Tom Schindl says:

        Sure tom dot schindl at bestsolution dot at

      • Hayden Jones says:

        I have sent the file, if you do not receive it let me know.

        Thanks.

      • Tom Schindl says:

        Received it but did not yet have time to take a look

  5. TPC says:

    Looks great! A maven plugin would be great, if you need help let me know.

  6. ssawan786 says:

    where can i get the latest fxml-compiler.zip or its related files. which files are needed from efxclipse nitely build area to update your distribution?

  7. Vimal raj says:

    I got this exception while trying to bulild.
    JRE : 1.7.40
    Eclipse Kepler.

    Buildfile: D:\Jack\Workspaces\JavaFX\CompilerTest\build.xml
    test:
    [delete] Deleting directory D:\Jack\Workspaces\JavaFX\CompilerTest\build
    [mkdir] Created dir: D:\Jack\Workspaces\JavaFX\CompilerTest\build\classes
    [javac] D:\Jack\Workspaces\JavaFX\CompilerTest\build.xml:25: warning: ‘includeantruntime’ was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 5 source files to D:\Jack\Workspaces\JavaFX\CompilerTest\build\classes
    [fxml-compiler] Compiling D:\Jack\Workspaces\JavaFX\CompilerTest\src\demo\Login.fxml

    BUILD FAILED
    D:\Jack\Workspaces\JavaFX\CompilerTest\build.xml:27: java.lang.NoSuchMethodError: java.lang.reflect.Constructor.getParameterCount()I

    Total time: 5 seconds

  8. Sven says:

    Hi there
    This is great work! However, the link to the nightly builds doesn’t work. Do you have another link?

  9. Gernot says:

    Hi! This sounds very promising, as I am also about to run into performance issues loading FXMLs. Is this project still alive? The download link is dead unfortunately.

  10. Johny HP says:

    Why did this useful tool dissapeared?

    • Tom Schindl says:

      It did not disappear but is still available from our eclipse download page

      • Johny HP says:

        Tom, first congrats for the great job you have done. However, the latest version you released(1.2.0) does not include the org.eclipse.fx, and moreover the ” org.eclipse.fx.ide.fxml.compiler.ant.FXMLCompilerTask, therefore I cannot compile. Have you released any version that supports “fx:include”?

      • Tom Schindl says:

        No we have not worked on fx:include IIRC

  11. Christian says:

    Hi Tom,

    I’ve got the same question like Johny HP. The FXMLCompiler seems to be absent from the org.eclipse.fx.fxml.compiler_1.2.0-SNAPSHOT.jar artifact. Is that an ommission or by design – maybe the package moved to some other artifact? If so into which one?

    Also, referring to:
    http://download.eclipse.org/efxclipse/?d

    … in folder:
    http://download.eclipse.org/efxclipse/compiler-released/1.1.0/?d

    … the artifact org.eclipse.fx.fxml.compiler_1.1.0-SNAPSHOT.jar does indeed contain the compiler package along with the ant task.

    So is 1.1.0 the latest usable Version or alternatively would it be worthwile to git clone https://git.eclipse.org/r/efxclipse/org.eclipse.efxclipse and build it myself? There the classes in question seem to be in https://git.eclipse.org/c/efxclipse/org.eclipse.efxclipse.git/tree/bundles/tooling/org.eclipse.fx.ide.fxml.compiler/src/org/eclipse/fx/ide/fxml/compiler.

    Best,
    Christian

    • Tom Schindl says:

      Yes you currently need to checkout the stuff from the repo. We wanted to provide a standalone app with 2.1.0 but did not have enough resources

  12. TT says:

    I tried running this in Eclipse with Ant using the FXML compiler from the version 2.1.0 e(fx)clipse plugin on Eclipse Mars and JDK 1.8.0_60, but I always get the following error:

    1) Error injecting constructor, org.eclipse.emf.common.util.WrappedException: java.lang.ClassCastException: org.eclipse.xtext.resource.XtextPlatformResourceURIHandler cannot be cast to org.eclipse.emf.ecore.xmi.XMLResource$URIHandler

    Parts of the stacktrace:
    Caused by: org.eclipse.emf.common.util.WrappedException: java.lang.ClassCastException: org.eclipse.xtext.resource.XtextPlatformResourceURIHandler cannot be cast to org.eclipse.emf.ecore.xmi.XMLResource$URIHandler
    at org.eclipse.xtext.parser.BaseEPackageAccess.loadResource(BaseEPackageAccess.java:57)
    […]
    at org.eclipse.fx.ide.fxgraph.FXGraphStandaloneSetupGenerated.createInjectorAndDoEMFRegistration(FXGraphStandaloneSetupGenerated.java:21)
    at org.eclipse.fx.ide.fxml.compiler.ant.CompilerTask.execute(CompilerTask.java:75)
    […]

    The last lines are the first time org.eclipse.fx appears in the trace.

    Any ideas? Is this to do with your plugin or is it an incompatibility between Eclipse plugins? Would you like me to email you the full stacktrace somewhere?

    • Tom Schindl says:

      We did not manage to update the fxml-compiler in 2.1.0 😦 – You can file a bug so that we fix this and svg2 fxml converter in 2.2.0

  13. Marcel says:

    There is a bug when the FXML has a tag inside.

    Example FXML part:

    ..
    The Exception is:
    org.xml.sax.SAXException: javafx.scene.layout.AnchorPane.getDefine()
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1261) ~[na:1.8.0_20]
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:649) ~[na:1.8.0_20]
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl.parse(SAXParserImpl.java:333) ~[na:1.8.0_20]
    at javax.xml.parsers.SAXParser.parse(SAXParser.java:195) ~[na:1.8.0_20]
    at org.eclipse.fx.ide.fxml.compiler.FXGraphCompiler.compileFXML(FXGraphCompiler.java:98) ~[compiler-0.9.0.jar:na]
    at com.proemion.linde.fdatool.FXMLToJava$1.visitFile(FXMLToJava.java:58) [classes/:na]
    at com.proemion.linde.fdatool.FXMLToJava$1.visitFile(FXMLToJava.java:1) [classes/:na]
    at java.nio.file.Files.walkFileTree(Files.java:2670) [na:1.8.0_20]
    at java.nio.file.Files.walkFileTree(Files.java:2742) [na:1.8.0_20]
    at com.proemion.linde.fdatool.FXMLToJava.main(FXMLToJava.java:47) [classes/:na]
    Caused by: java.lang.NoSuchMethodException: javafx.scene.layout.AnchorPane.getDefine()
    at java.lang.Class.getMethod(Class.java:1773) ~[na:1.8.0_20]
    at org.eclipse.fx.ide.fxml.compiler.ReflectionHelper.getValueType(ReflectionHelper.java:404) ~[compiler-0.9.0.jar:na]
    at org.eclipse.fx.ide.fxml.compiler.FXMLSaxHandler.startElement(FXMLSaxHandler.java:170) ~[compiler-0.9.0.jar:na]
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:509) ~[na:1.8.0_20]
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:379) ~[na:1.8.0_20]
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2786) ~[na:1.8.0_20]
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606) ~[na:1.8.0_20]
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:117) ~[na:1.8.0_20]
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510) ~[na:1.8.0_20]
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848) ~[na:1.8.0_20]
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777) ~[na:1.8.0_20]
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) ~[na:1.8.0_20]
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213) ~[na:1.8.0_20]
    … 9 common frames omitted

  14. Karan Mandal says:

    Here is new Netbeans plugin to generate java class from fxml document…

    http://plugins.netbeans.org/plugin/68619/fxml-2-java-convertor

  15. Any updates on this? 😉

  16. Suraj Dangat says:

    Can we have support for fxml to java for javafx8 if yes please provide necessary jar and latest sample example with javafx8

Leave a reply to Tom Schindl Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.