Chart component customisation

I have been attempting to created a stacked line chart using the ‘Chart’ component, using the ‘configureChart’ extension function. But I am unable to get it to work. The extension function code is as follows:

from org.jfree.chart.renderer.xy import StackedXYAreaRenderer
plot = chart.getPlot()
renderer = plot.getRenderer()
plot.setRenderer(StackedXYAreaRenderer())

Which then throws the following ClassCastException error which I am unable to fix:

15:57:20.892 [AWT-EventQueue-2] ERROR Vision.ClassicChart - Error invoking extension method.
org.python.core.PyException: Traceback (most recent call last):
  File "<extension-method configureChart>", line 30, in configureChart
	at org.jfree.chart.renderer.xy.StackedXYAreaRenderer.findRangeBounds(StackedXYAreaRenderer.java:359)

	at org.jfree.chart.plot.XYPlot.getDataRange(XYPlot.java:4057)
	at org.jfree.chart.axis.NumberAxis.autoAdjustRange(NumberAxis.java:426)
	at org.jfree.chart.axis.NumberAxis.configure(NumberAxis.java:409)
	at org.jfree.chart.plot.XYPlot.configureRangeAxes(XYPlot.java:1169)
	at org.jfree.chart.plot.XYPlot.setRenderer(XYPlot.java:1470)
	at org.jfree.chart.plot.XYPlot.setRenderer(XYPlot.java:1445)
	at org.jfree.chart.plot.XYPlot.setRenderer(XYPlot.java:1432)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)


java.lang.ClassCastException: java.lang.ClassCastException: com.inductiveautomation.factorypmi.application.components.chart.runtime.XYNumericDataSet cannot be cast to org.jfree.data.xy.TableXYDataset

	at org.python.core.Py.JavaError(Py.java:495)
	at org.python.core.Py.JavaError(Py.java:488)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:188)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:204)
	at org.python.core.PyObject.__call__(PyObject.java:404)
	at org.python.core.PyObject.__call__(PyObject.java:408)
	at org.python.core.PyMethod.__call__(PyMethod.java:124)
	at org.python.pycode._pyx406.configureChart$1(<extension-method configureChart>:30)
	at org.python.pycode._pyx406.call_function(<extension-method configureChart>)
	at org.python.core.PyTableCode.call(PyTableCode.java:165)
	at org.python.core.PyBaseCode.call(PyBaseCode.java:301)
	at org.python.core.PyFunction.function___call__(PyFunction.java:376)
	at org.python.core.PyFunction.__call__(PyFunction.java:371)
	at org.python.core.PyFunction.__call__(PyFunction.java:361)
	at org.python.core.PyFunction.__call__(PyFunction.java:356)
	at com.inductiveautomation.vision.api.client.components.model.ExtensionFunction.invoke(ExtensionFunction.java:154)
	at com.inductiveautomation.factorypmi.application.components.PMIChart.createChartImpl(PMIChart.java:480)
	at com.inductiveautomation.factorypmi.application.components.chart.PMILineChartPanel.createChart(PMILineChartPanel.java:128)
	at com.inductiveautomation.factorypmi.application.components.PMIChart.setExtensionFunctions(PMIChart.java:465)
	at com.inductiveautomation.factorypmi.designer.eventhandling.ComponentScriptEditor.applyChanges(ComponentScriptEditor.java:602)
	at com.inductiveautomation.factorypmi.designer.eventhandling.ComponentScriptEditor.access$600(ComponentScriptEditor.java:87)
	at com.inductiveautomation.factorypmi.designer.eventhandling.ComponentScriptEditor$5.actionPerformed(ComponentScriptEditor.java:342)
	at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
	at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
	at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
	at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at javax.swing.JComponent.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.awt.EventQueue.access$500(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue$4.run(Unknown Source)
	at java.awt.EventQueue$4.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.ClassCastException: com.inductiveautomation.factorypmi.application.components.chart.runtime.XYNumericDataSet cannot be cast to org.jfree.data.xy.TableXYDataset
	at org.jfree.chart.renderer.xy.StackedXYAreaRenderer.findRangeBounds(StackedXYAreaRenderer.java:359)
	at org.jfree.chart.plot.XYPlot.getDataRange(XYPlot.java:4057)
	at org.jfree.chart.axis.NumberAxis.autoAdjustRange(NumberAxis.java:426)
	at org.jfree.chart.axis.NumberAxis.configure(NumberAxis.java:409)
	at org.jfree.chart.plot.XYPlot.configureRangeAxes(XYPlot.java:1169)
	at org.jfree.chart.plot.XYPlot.setRenderer(XYPlot.java:1470)
	at org.jfree.chart.plot.XYPlot.setRenderer(XYPlot.java:1445)
	at org.jfree.chart.plot.XYPlot.setRenderer(XYPlot.java:1432)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:186)
	... 55 common frames omitted
java.lang.ClassCastException: com.inductiveautomation.factorypmi.application.components.chart.runtime.AutoAnnotateXYPlot$IntervalEmulatingXYDataset cannot be cast to org.jfree.data.xy.TableXYDataset
	at org.jfree.chart.renderer.xy.StackedXYAreaRenderer.drawItem(StackedXYAreaRenderer.java:406)
	at com.inductiveautomation.factorypmi.application.components.chart.runtime.AutoAnnotateXYPlot.render(AutoAnnotateXYPlot.java:170)
	at com.inductiveautomation.factorypmi.application.components.chart.runtime.AutoAnnotateXYPlot.draw(AutoAnnotateXYPlot.java:639)
	at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1227)
	at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1361)
	at com.inductiveautomation.factorypmi.application.components.chart.PMILineChartPanel.paintComponent(PMILineChartPanel.java:210)
	at com.inductiveautomation.factorypmi.application.components.PMIChart.paintComponent(PMIChart.java:286)
	at javax.swing.JComponent.paint(Unknown Source)
	at javax.swing.JComponent.paintChildren(Unknown Source)
	at com.inductiveautomation.vision.api.client.components.model.AbstractVisionPanel.paintChildren(AbstractVisionPanel.java:272)
	at javax.swing.JComponent.paint(Unknown Source)
	at javax.swing.JComponent.paintChildren(Unknown Source)
	at javax.swing.JComponent.paint(Unknown Source)
	at javax.swing.JLayeredPane.paint(Unknown Source)
	at javax.swing.JComponent.paintChildren(Unknown Source)
	at javax.swing.JComponent.paint(Unknown Source)
	at javax.swing.JComponent.paintChildren(Unknown Source)
	at javax.swing.JComponent.paint(Unknown Source)
	at com.inductiveautomation.ignition.designer.designable.DesignPanel$DesignableContainerLayer.paintChildren(DesignPanel.java:1222)
	at javax.swing.JComponent.paint(Unknown Source)
	at javax.swing.JComponent.paintChildren(Unknown Source)
	at javax.swing.JComponent.paint(Unknown Source)
	at javax.swing.JComponent.paintChildren(Unknown Source)
	at javax.swing.JComponent.paint(Unknown Source)
	at javax.swing.JViewport.paint(Unknown Source)
	at javax.swing.JComponent.paintChildren(Unknown Source)
	at javax.swing.JComponent.paint(Unknown Source)
	at javax.swing.JComponent.paintToOffscreen(Unknown Source)
	at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
	at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
	at javax.swing.RepaintManager.paint(Unknown Source)
	at javax.swing.JComponent._paintImmediately(Unknown Source)
	at javax.swing.JComponent.paintImmediately(Unknown Source)
	at javax.swing.RepaintManager$4.run(Unknown Source)
	at javax.swing.RepaintManager$4.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
	at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
	at javax.swing.RepaintManager.prePaintDirtyRegions(Unknown Source)
	at javax.swing.RepaintManager.access$1200(Unknown Source)
	at javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
	at java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.awt.EventQueue.access$500(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)

Any wisdom on what can be done?

You’ll have to make a subclass of StackedXYAreaRenderer that accepts an ordinary XYDataset on all relevant methods. Each of those methods will have to verify that the XYDataset meets the requirements of a TableXYDataset, and convert it to pass to the superclass methods. And probably cache the conversion to go fast.