NullPointerException in clearRangeAxes

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
mchericoni
Posts: 1
Joined: Thu Nov 15, 2007 9:01 am
Location: Bologna

NullPointerException in clearRangeAxes

Post by mchericoni » Wed Feb 13, 2008 10:59 am

I receive the following NullPointerException when trying to clear range axes for a TimeSeriesChart.
I verified that I have 1 range axis and 1 domain axis:
getRangeAxisCount -> 1
getDomainAxisCount -> 1
Anyway I don't expect to get an exception, even if there is no range axis in my chart: in this case the method should simply do nothing.

I just make this call:

Code: Select all

chart.getXYPlot().clearRangeAxes();
I used a test button to make that call, but I'm always getting the following exception:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at org.jfree.chart.plot.XYPlot.drawQuadrants(XYPlot.java:2790)
at org.jfree.chart.plot.XYPlot.drawBackground(XYPlot.java:2765)
at org.jfree.chart.plot.XYPlot.draw(XYPlot.java:2530)
at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1175)
at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1265)
at javax.swing.JComponent.paint(JComponent.java:1006)
at javax.swing.JComponent.paintWithOffscreenBuffer(JComponent.java:4972)
at javax.swing.JComponent.paintDoubleBuffered(JComponent.java:4925)
at javax.swing.JComponent._paintImmediately(JComponent.java:4868)
at javax.swing.JComponent.paintImmediately(JComponent.java:4675)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:451)
at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:114)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:461)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)

Mike Watts
Posts: 13
Joined: Mon May 08, 2006 1:27 pm

Post by Mike Watts » Thu Jul 24, 2008 6:17 pm

Was there any resolution to this - I have been battling the same problem all afternoon. When I set up the XYPlot for the first time, I call clearRangeAxes() anyway which works fine. If the user should change what they want on the graph I need to refresh it and calling clearRangeAxes() a subsequent time causes the NPE noted above ( at XYPlot.drawQuadrants )

Any thoughts, anyone?

Mike Watts
Posts: 13
Joined: Mon May 08, 2006 1:27 pm

Post by Mike Watts » Thu Jul 24, 2008 6:20 pm

Oh yes one more thing - this is updated code which originally worked with 1.0.5 so it may be related to a change since then...

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Post by paradoxoff » Fri Jul 25, 2008 7:28 am

Which version of JFreeChart are you using ? I just checked the code in the online API doc, and line 2790 of XYPlot doe not belong to drawQuadrants, so it is difficult to say what reason is really responsible for the NPE.

Even without knowing the exact context, it is possible to get a vague idea about what might be happening. First of all, the NPE is not triggered by the clearRangeAxes method itself. That seems to execute just fine.
But clearRangeAxes is calling fireChangeEvent() at the end which is finally triggering a redraw of the chart, and apparently it is the redraw (more specifically the drawQuadrants method) that results in the NPE.
Looking at this method, it is clear that a "real" (i.e. a non-null) domain and range axis is required. But where should the latter come from when you have just cleared the range axes list?

I think that clearRangeAxes should only be called if the chart containing the plot is not shown. And after clearRangeAxes has been called, a new range axis needs to be assigned to the plot before it is made visible. I would be interested to see how you set up the XYPlot for the first time and call clearRangeAxes w/o problems. Is the plot already visible at that point?

The only way to circumvent the NPE is that the changes that the users "want on the graph" are done by changing existing objects or more specifically axes instead of throwing things away (and automatically trigger a repaint before you even have a chance to install new ones).
Regards, paradoxoff

Mike Watts
Posts: 13
Joined: Mon May 08, 2006 1:27 pm

Post by Mike Watts » Fri Jul 25, 2008 11:42 am

Many thanks for your reply. I am using version 1.0.9 and the stack trace I have actually quotes line 2924 in drawQuadrants ( I can post the complete trace if you wish ).

I agree with your likely root cause, and it is true that I first call clearRangeAxes before the plot is visible ( i.e. added to JFreeChart and to a ChartPanel ) but I tried calling it after it is visible and still, the first time it does not cause the problem. Also of some interest is that the chart seems to work okay anyway, but obviously throwing away the exception is not very good practice without having better understanding. My code extends XYPlot and there is no setVisible method - hiding it when refreshing would require a callback to ChartPanel.

I'm happy to post the complete code here but this is the scheme:
Setting up the chart for the first time:
a single range axis is set up with a renderer and domain axis ( actually two date axes - I'm trying to make it look like the period axis without the problems when zooming ;-) )

Refreshing the traces - this is called when items are added to a list for the first time or after the list has changed - I would prefer to just remove the extra range axes ( so 1 always remains ) but that isn't an option:

clearRangeAxes()
for each dataset apart from the primary:
setDataset(i, null)
setRenderer(i,null)

for each new dataset [including primary]:
setDataset(i, timeSeriesCollection)
setRangeAxis(i, rangeAxis)
mapDatasetToRangeAxis(i, i)
setRangeAxisLocation(left if i=0 or 2, otherwise right)
setRenderer(i, standardXYItemRenderer)
renderer.setGapThresholdType(absolute)
renderer.setGapThreshold(gap)
renderer.setBaseShapesVisible(false)
renderer.setDrawSeriesLineAsPath(true)
for each line in the set:
renderer.setSeriesPaint(color)
renderer.setseriesStroke(basicStroke)

Regards, Mike

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Fri Jul 25, 2008 1:07 pm

Can you try JFreeChart 1.0.10, because I vaguely recall fixing a bug like this one...
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Fri Jul 25, 2008 2:30 pm

david.gilbert wrote:Can you try JFreeChart 1.0.10, because I vaguely recall fixing a bug like this one...
OK, I just checked this myself and it fails in 1.0.10 also. This is a bug, and I will fix it for the next release.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

Mike Watts
Posts: 13
Joined: Mon May 08, 2006 1:27 pm

Post by Mike Watts » Fri Jul 25, 2008 3:09 pm

David - thanks for responding - doesn't appear to make any difference though - here is the stack trace now:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at org.jfree.chart.plot.XYPlot.drawQuadrants(XYPlot.java:3048)
at org.jfree.chart.plot.XYPlot.drawBackground(XYPlot.java:3024)
at org.jfree.chart.plot.XYPlot.draw(XYPlot.java:2789)
at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1224)
at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1359)
at javax.swing.JComponent.paint(JComponent.java:1022)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5104)
at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1386)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1317)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:301)
at javax.swing.RepaintManager.paint(RepaintManager.java:1132)
at javax.swing.JComponent._paintImmediately(JComponent.java:5052)
at javax.swing.JComponent.paintImmediately(JComponent.java:4862)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:727)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:683)
at javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:663)
at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:128)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:121)

I guess the way forward is for me to override the method and remove all but the primary axis unless you have a better suggestion?

Regards, Mike

Mike Watts
Posts: 13
Joined: Mon May 08, 2006 1:27 pm

Post by Mike Watts » Mon Jul 28, 2008 12:49 pm

I'm not an expert on how JFreeChart works so this may be less than optimal but anyway here are some workarounds that may be of interest to others:

1. replace the current clearRangeAxes with:

public void clearRangeAxes() {
for (int i = 0; i < this.rangeAxes.size(); i++) {
ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
if (axis != null) {
axis.removeChangeListener(this);
}
}
rangeAxes.clear();
ValueAxis primary = new org.jfree.chart.axis.NumberAxis();
setRangeAxis(0, primary, false);
}

2. stop repainting if an axis is null - the following can be added after the first test in the "draw" method:

// if either axis is null, just return...
if ( getDomainAxis() == null || getRangeAxis() == null )
return;

3. prevent repainting until the plot is valid - this needs to be done on the JFreeChart containing the plot ( assuming the chart is double-buffered ):

chart.setNotify(false);
// do the update to the plot including call to clearRangeAxes and
// setting up the new axes
chart.setNotify(true);
chart.fireChartChanged();

the last option gives the advantage that the view switches from the old to the new with no intermediate stages of default axes or missing plots which you see with options 1 & 2.

Hope someone finds this of use!

Mike Watts

Locked