Geertjan is a DZone Zone Leader and has posted 467 posts at DZone. You can read more from them at their website. View Full User Profile

How to Render XML in JavaFX Charts

04.21.2013
| 3846 views |
  • submit to reddit

Let's say you have XML files, that come out of some kind of process, that look like this:

<graph caption="Monthly Revenue (in US Dollars)">
  <set name="Jan" value="25000" />
  <set name="Feb" value="35000" />
  <set name="Mar" value="42300" />
  <set name="Apr" value="35300" />
  <set name="May" value="31300" />
  <set name="Jun" value="37300" />
</graph>

Now we want to create an XML editor for the above file, that looks like this:

At the same time, we want to visualize/render the XML file graphically, for which JavaFX charts are absolutely brilliant. They're simple to use and they allow for real-time animation, which is cool. Here's an example of how the above can be rendered in a JavaFX bar chart:

When changes are detected in the underlying XML file, the chart is automatically updated to reflect the change.

The question is how to create the above application. The sources of the above (minus jfxrt.jar, which I had to exclude from the check in because it was too large) is here:

https://java.net/projects/nb-api-samples/sources/api-samples/show/versions/7.3/misc/MySimpleGraphProcessor

Here are the steps to get to the above point:

  1. Create the Basis. Create a new NetBeans Platform application and add a few extra modules (from the application's Libraries tab in the Project Properties dialog), specifically, from the "ide" cluster the "User Utilities" and the "XML Text Editor" modules, together with their required dependencies, as well as the "Favorites" module from the "platform" cluster.

  2. Recognize the File Type. Create a module in your application. Use the NetBeans File Type Tutorial  to generate the code needed to recognize your XML file by its namespace or root name (in this case, as can be seen from the XML file at the top of this article the root name is "graph"). Include a MultiView Element, via the checkbox in the wizard, which is where the JavaFX chart will be displayed.

  3. Include the JavaFX Runtime. Create a NetBeans library wrapper module to include "jfxrt.jar" and set a dependency on it in the module described above.

  4. Add Code to the MultiView Element. All the coding you need to do is done in the MultiView Element. Here you need to parse the XML file and render the JavaFX graph. Below is all the relevant code, minus the import statements and annotations at the top of the class and the standard generated code at the end.
    public final class MySimpleGraphVisualElement extends JPanel implements MultiViewElement {
    
        private MySimpleGraphDataObject obj;
        private transient MultiViewElementCallback callback;
        private List<String> names;
        private List<String> values;
        private String caption = "";
        private FileObject fo;
    
        public MySimpleGraphVisualElement(Lookup lkp) {
            obj = lkp.lookup(MySimpleGraphDataObject.class);
            assert obj != null;
            initComponents();
            setLayout(new BorderLayout());
            parseFile();
            initJavaFX();
        }
    
        public void initJavaFX() {
            final JFXPanel chartFxPanel = new JFXPanel();
            add(chartFxPanel, BorderLayout.CENTER);
            Platform.setImplicitExit(false);
            Platform.runLater(new Runnable() {
                @Override
                public void run() {
                    Scene scene = new Scene(createBarChart());
                    chartFxPanel.setScene(scene);
                }
            });
        }
    
        private void parseFile() {
            fo = obj.getPrimaryFile();
            names = new ArrayList();
            values = new ArrayList();
            InputSource source;
            try {
                source = new InputSource(fo.getInputStream());
                Document doc = XMLUtil.parse(source, false, false, null, null);
                NodeList graphNodeList = doc.getElementsByTagName("graph");
                NamedNodeMap graphNodeMap = graphNodeList.item(0).getAttributes();
                for (int j = 0; j < graphNodeMap.getLength(); j++) {
                    Node attrNode = graphNodeMap.item(j);
                    String attrName = attrNode.getNodeName();
                    if (attrName.equals("caption")) {
                        caption = attrNode.getNodeValue();
                    }
                }
                NodeList setNodeList = doc.getElementsByTagName("set");
                for (int i = 0; i < setNodeList.getLength(); i++) {
                    Node setNode = setNodeList.item(i);
                    NamedNodeMap map = setNode.getAttributes();
                    for (int j = 0; j < map.getLength(); j++) {
                        Node attrNode = map.item(j);
                        String attrName = attrNode.getNodeName();
                        if (attrName.equals("name")) {
                            names.add(attrNode.getNodeValue());
                        } else if (attrName.equals("value")) {
                            values.add(attrNode.getNodeValue());
                        }
                    }
                }
            } catch (IOException ex) {
                Exceptions.printStackTrace(ex);
            } catch (SAXException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
    
        private BarChart createBarChart() {
            final ObservableList<BarChart.Series> bcData = 
                    FXCollections.<BarChart.Series>observableArrayList();
            for (int column = 0; column < names.size(); column++) {
                ObservableList<BarChart.Data> series = 
                        FXCollections.<BarChart.Data>observableArrayList();
                series.add(
                        new BarChart.Data(names.get(column), 
                        Integer.parseInt(values.get(column))));
                bcData.add(new BarChart.Series(series));
            }
            CategoryAxis xAxis = new CategoryAxis();
            xAxis.setCategories(FXCollections.observableArrayList(names));
            NumberAxis yAxis = new NumberAxis();
            yAxis.setTickUnit(10000);
            final BarChart chart = new BarChart(xAxis, yAxis, bcData);
            chart.setTitle(caption);
            fo.addFileChangeListener(new FileChangeAdapter() {
                @Override
                public void fileChanged(FileEvent fe) {
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            parseFile();
                            for (int column = 0; column < values.size(); column++) {
                                XYChart.Series<String, Number> s = 
                                        (XYChart.Series<String, Number>) chart.getData().get(column);
                                BarChart.Data data = s.getData().get(0);
                                data.setYValue(Integer.parseInt(values.get(column)));
                            }
                        }
                    });
                }
            });
            return chart;
        }
        
        ...
        ...
        ...

The above is literally all the code that you need to add yourself. All the rest is part of the NetBeans Platform or generated by wizards in NetBeans IDE. For example, you're automatically able to open multiple charts and compare them in different ways, e.g:

Of course, the charts can be undocked from the main frame and moved around, e.g., onto different monitors. That's all pretty cool.

Published at DZone with permission of its author, Geertjan Wielenga.