1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
skunk
Posts: 1087
Joined: Thu Jun 02, 2005 10:14 pm
Location: Brisbane, Australia

1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by skunk » Sat Jan 10, 2009 4:59 pm

While attempting to upgrade to JFreeChart 1.0.12 I came across the following problem with some new code added to refreshTicksHorizontal() in class org.jfree.chart.axis.DateAxis

Code: Select all

1620    
1621            while (tickDate.before(upperDate)) {
1622                // could add a flag to make the following correction optional...
1623                tickDate = correctTickDateForPosition(tickDate, unit,
1624                        this.tickMarkPosition);
1625    
1626                long lowestTickTime = tickDate.getTime();
1627                long distance = unit.addToDate(tickDate, this.timeZone).getTime()
1628                        - lowestTickTime;
1629                for(int minorTick = 1; minorTick < getMinorTickCount();
1630                        minorTick++) {
1631                    long minorTickTime = lowestTickTime - distance
1632                            * minorTick / getMinorTickCount();
1633                    if (minorTickTime > 0 && getRange().contains(minorTickTime)
1634                            && (!isHiddenValue(minorTickTime))) {
1635                        result.add(new DateTick(TickType.MINOR,
1636                                new Date(minorTickTime), "", TextAnchor.TOP_CENTER,
1637                                TextAnchor.CENTER, 0.0));
1638                    }
1639                }
1640   
1641                if (!isHiddenValue(tickDate.getTime())) {
1642                    // work out the value, label and position
At least in my code, the loop starting at line 1621 never completes in the case when a weekdays only segmented timeline is applied and the tickUnit is daily or larger because the tickDate is continually adjusted backwards.

The only way I was able to correct this issue was to move the block of code from lines 1622-1639 that "corrects" the tickDate out of the enclosing loop -- essentially moving line 1621 to line 1640.

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 » Sun Jan 11, 2009 9:15 pm

Thanks for the report. I'll look at this during the week...the first thing I'd like to do is reproduce this with a JUnit test so that after it is fixed it doesn't get broken again. Anyway, I've opened a bug report at SourceForge:

https://sourceforge.net/tracker2/?func= ... tid=115494
David Gilbert
JFreeChart Project Leader

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

chalky
Posts: 1
Joined: Sun Feb 22, 2009 4:19 pm

1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by chalky » Sun Feb 22, 2009 6:03 pm

I've also encountered this problem. Here's a couple of things that I've noticed.
  1. When the date range is a year or less, the code never goes into an infinite loop. When the date range is greater than 1 year and a few months, it always goes into an infinite loop. Here's a test that can be added to org.jfree.chart.axis.junit.DateAxisTests to demonstrate this.

    Code: Select all

    public void testBug2499965AxisRange() {
    
        DateAxis axis = new DateAxis();
        axis.setTimeline(SegmentedTimeline.newMondayThroughFridayTimeline());
        axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
        axis.setDateFormatOverride(new SimpleDateFormat("dd MMM yy"));
    
        Year y2006 = new Year(2006);
        Year y2007 = new Year(2007);
    
        Date d0 = new Date(y2006.getFirstMillisecond());
        Date d1 = new Date(y2007.getFirstMillisecond());
        Date d2 = new Date(y2007.getLastMillisecond());
            
        BufferedImage image = new BufferedImage(1920, 1200, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = image.createGraphics();
        Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, 1920, 1200);
    
        // Firstly test that everything's ok when the client specifies the tick unit
        axis.setRange(d0, d1);
            
        // This call to refreshTicks successfully returns
        axis.refreshTicks(g2, new AxisState(), area, RectangleEdge.BOTTOM);
                    
        // This call to refreshTicks goes into an infinite loop        
        axis.setRange(d0, d2);
        System.out.println("About to call refreshTicks() with a 2 year date range");
        axis.refreshTicks(g2, new AxisState(), area, RectangleEdge.BOTTOM);
    }    
    
  2. When the axis's date range is more than a year, this bug can be suppressed by setting autoTickUnitSelection to false. Again here's the test code.

    Code: Select all

    public void testBug2499965AutoTickUnitSelection() {
    
        DateAxis axis = new DateAxis();
        axis.setTimeline(SegmentedTimeline.newMondayThroughFridayTimeline());
        axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
        axis.setDateFormatOverride(new SimpleDateFormat("dd MMM yy"));
    
        Year y2006 = new Year(2006);
        Year y2007 = new Year(2007);
    
        Date d0 = new Date(y2006.getFirstMillisecond());
        Date d2 = new Date(y2007.getLastMillisecond());
            
        BufferedImage image = new BufferedImage(1920, 1200, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = image.createGraphics();
        Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, 1920, 1200);
    
        // Firstly test that everything's ok when the client specifies the tick unit
        axis.setRange(d0, d2);
        axis.setAutoTickUnitSelection(false);
        axis.setTickUnit(new DateTickUnit(DateTickUnit.MONTH, 1));
            
        // This call to refreshTicks successfully returns
        axis.refreshTicks(g2, new AxisState(), area, RectangleEdge.BOTTOM);
                    
        // This call to refreshTicks goes into an infinite loop        
        axis.setAutoTickUnitSelection(true);
        System.out.println("About to call refreshTicks() with autoTickUnitSelection = true");
        axis.refreshTicks(g2, new AxisState(), area, RectangleEdge.BOTTOM);
    }  
    
Hope this helps
Chris

skunk
Posts: 1087
Joined: Thu Jun 02, 2005 10:14 pm
Location: Brisbane, Australia

Post by skunk » Sun Feb 22, 2009 8:58 pm

Thank you very much for the test case. This should ensure that an appropriate fix can be added soon.

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

Re: 1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by david.gilbert » Fri Feb 27, 2009 6:32 pm

I started on a fairly ambitious reworking of the date tick unit mechanism, that I hope will resolve this bug...but I've stalled at the moment. I will get back into this next week. Thanks for the JUnit tests though, they're very useful.

I want to release JFreeChart 1.0.13 in March (actually, I had hoped for February, but it will have to be March now) and this bug is one that has to be fixed before the release goes out.
David Gilbert
JFreeChart Project Leader

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

shaunkalley
Posts: 2
Joined: Mon May 18, 2009 7:44 pm

Re: 1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by shaunkalley » Mon May 18, 2009 7:46 pm

This issue is still present in 1.0.13.

skunk
Posts: 1087
Joined: Thu Jun 02, 2005 10:14 pm
Location: Brisbane, Australia

Re: 1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by skunk » Mon May 18, 2009 8:17 pm

Yup

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

Re: 1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by david.gilbert » Fri May 22, 2009 8:28 am

Sorry for this one. I was aware that I hadn't fixed it as promised, but a paying customer needed an "official" JFreeChart release for another bug fix, so I went ahead and made the 1.0.13 release anyway. The fix I was working on involves some bigger changes to the date tick unit mechanism that will fix some other bugs too.
David Gilbert
JFreeChart Project Leader

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

dude
Posts: 1
Joined: Mon May 25, 2009 10:05 am

Re: 1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by dude » Mon May 25, 2009 10:31 am

Are these bigger changes already available in subversion? I'd like to try them out as I'm getting bitten by this bug. I don't need an "official" release, but as a paying customer I'd like to have access to a better solution than the workarounds described earlier in this post.

ssingh
Posts: 2
Joined: Sun Sep 06, 2009 1:42 pm
antibot: No, of course not.

Re: 1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by ssingh » Mon Sep 07, 2009 6:12 pm

Hi,

I am facing the same problem, I am using the current version 1.0.13
The chart hungs up for dataset around one year or more, but works fine for lesser data.

Please let me know if there is a solution in place already for this.

Also, I used this workaround " dateAxis.setAutoTickUnitSelection(false); " , but it was showing grid lines for each data entry.
I used , dateAxis.setTickUnit(new DateTickUnit(DateTickUnitType.MONTH, 1)); , but it doesnt show tick labels for months for which weekend falls on the 1st of the Month.

Please help with the workaround , if the solution is not in place already.

Code: Select all

 
final CombinedDomainXYPlot plot = new CombinedDomainXYPlot(dateAxis);
      dateAxis.setTimeline(SegmentedTimeline.newMondayThroughFridayTimeline());
      dateAxis.setAutoTickUnitSelection(false);
      dateAxis.setTickUnit(new DateTickUnit(DateTickUnitType.MONTH, 1));
   
Thanks
--

OlivierPisano
Posts: 1
Joined: Thu Nov 05, 2009 9:23 am
antibot: No, of course not.

Re: 1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by OlivierPisano » Thu Nov 05, 2009 9:42 am

Hi, I am currently facing this problem with version 1.0.13 too.
I tried to switch back to 1.0.11 but it didn't help much. Does somebody know about the version this bug first occurred ?

Mr Gilbert, is it worth spending time trying to figure how to circumvent the bug or is a release including a fix for it to be expected soon ?

Regards,

Olivier.

davesnowdon
Posts: 9
Joined: Tue Jul 06, 2010 7:10 pm
antibot: No, of course not.

Re: 1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by davesnowdon » Tue Oct 19, 2010 12:58 pm

I've also just encountered the bug. Any news on a fix?

Dave, this is the first time I've posted to the forums so I'd like take the opportunity to thank you for all your work on JFreeChart. I bought the developer guide a couple of months ago but am only now finding the time to really get started with JFreeChart.

cheers

Dave

sw3170
Posts: 3
Joined: Tue Dec 21, 2010 3:40 pm
antibot: No, of course not.

Re: 1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by sw3170 » Tue Dec 21, 2010 4:38 pm

I'm having this problem too. ....err....
However unlike some mentioned above, I'm running into this problem with date range of only 6 months. Here's where my problem is

In skunk's original posted code, line 1623
tickDate = correctTickDateForPosition(tickDate, unit,
this.tickMarkPosition);

It keeps changing tickDate back to its old value, hence creates an endless loop.

davesnowdon
Posts: 9
Joined: Tue Jul 06, 2010 7:10 pm
antibot: No, of course not.

Re: 1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(...)

Post by davesnowdon » Wed Mar 23, 2011 12:52 pm

sw3170 wrote:I'm having this problem too. ....err....
However unlike some mentioned above, I'm running into this problem with date range of only 6 months. Here's where my problem is

In skunk's original posted code, line 1623
tickDate = correctTickDateForPosition(tickDate, unit,
this.tickMarkPosition);

It keeps changing tickDate back to its old value, hence creates an endless loop.
The fix I posted in this thread appears to solve this problem too:
http://www.jfree.org/phpBB2/viewtopic.php?f=3&t=38026

Dave

avarvit
Posts: 3
Joined: Mon May 21, 2012 3:07 pm
antibot: No, of course not.

Re: 1.0.12 Infinite loop in DateAxis.refreshTicksHorizontal(

Post by avarvit » Mon May 21, 2012 3:21 pm

I just run into the same bug in 1.0.13. It seems to have stayed unresolved in 1.0.14 (have not debugged, but my server still hangs). I have debugged 1.0.13 and came up with the following information and bugfix.

The bug shows up when using a segmented timeline or a timeline with exceptions. It may show up in other cases as well, but I have not been able to reproduce it without a segmented timeline.

In my case, the bug is triggered because, when the TickUnit, instead of DAY, is MONTH or YEAR, correctTickDateForPosition() is called and it may return a prior day to that of tickDate (its argument). Then further down, isHiddenValue() is checked. In the (rare) case that isHiddenValue returns true, the code jumps to line ~1817 (it's my patched source, so it may differ a bit from the official 1.0.13), where rollDate() is called. Now check this. April 1st was on a Sunday. It just so happens that rollDate() returns April 2. Then correctTickDateForPosition() resets this to April 1st. Then isHiddenValue() returns false for that, hence rollDate() is called again. And so forth, ad infinitum.

Here how the above example works:

Code: Select all

    protected List refreshTicksHorizontal(Graphics2D g2,
                Rectangle2D dataArea, RectangleEdge edge) {
        ...
        while (tickDate.before(upperDate)) {
            // could add a flag to make the following correction optional...
            tickDate = correctTickDateForPosition(tickDate, unit,
                    this.tickMarkPosition);            // <---------- this returns April 1st 2012
            ...
            if (!isHiddenValue(tickDate.getTime())) {    // <----- isHiddenValue(April 1st) is on a Sunday, hence we get true
            ... (code that never gets executed
            }
            else { // control jumps here
                tickDate = unit.rollDate(tickDate, this.timeZone); // <------------ this returns April 2nd again
                continue; // this jumps over, completing the infinite loop between April 1st and 2nd!
            }
I have been able to remedy the situation by means of this patch (the lines after //avarvit are mine) in correctTickDateForPosition:

Code: Select all

            case (DateTickUnit.MONTH) :
                result = calculateDateForPosition(new Month(time,
                        this.timeZone, this.locale), position);
	            // avarvit
	            if (result.getTime() < time.getTime()) result = time;
                break;
            case(DateTickUnit.YEAR) :
                result = calculateDateForPosition(new Year(time,
                        this.timeZone, this.locale), position);
	            // avarvit
	            if (result.getTime() < time.getTime()) result = time;
                break;
Perhaps this is not the most correct of all possible fixes (e.g., one might need to test for isHiddenValue() inside correctTickDateForPosition()), however I think it will offer some insight. After the fix, my chart displays a tick for April 2nd instead of April 1st and no infinite loop occurs. I hope this helps!

Locked