jzy3d/jzy3d-api — pull request #29 by laurentpsychedelic (2013-08-11 10:31:05+00:00)
Title: The comparison method used in PointOrderStrategy#compare(Coord3d, Coord3...

Dear Martin,
I managed to catch an error when changing a graph's scale by right button mouse drag, related to an exception thrown by the new Collections.sort algorithm used in JDK7. In my application, it causes an uncaught exception, the graph being frozen from that point. Below is a part of the commit log message of the commit in which I managed to correct the error. The solution is simple: not forgetting returning 0 for equal objects in the comparator (it already did that when camera==null but not when an actual camera is used for the ordering!).
I added a test that demonstrates the problem for the old version of PointOrderingStrategy (however it fails only on JDK7).

commit 36470b61e97a52eb1e06493fd66ec248fc207680
Author: Laurent FABRE
Date: Sat Aug 10 14:13:33 2013

The comparison method used in PointOrderStrategy#compare(Coord3d, Coord3d)
violates the comparison contract used by the new collection sorting algorithm
of JDK/JRE7 (TimSort).

Writing:
if (d1 B AND B > A are true at the same time for a
certain set of inputs A and B (in fact for all A and B such as A = B).
The condition A > B if, and only if B < A is not satisfied.
The reason is that two equal objects are not recognized as being equal
by the previous implementation of PointOrderingStrategy#compare.

Up to JDK/JRE6, the default algorithm used for sorting collection (MergeSort)
does not care about the reflexivity condition and sort the collection given
as input without throwing an error, however the new TimSort algorithm throws
an exception when it finds that the Comparator provided to sort the collection
violates the reflexivity condition.

java.lang.IllegalArgumentException: Comparison method violates its general contract!

java.util.TimSort.mergeHi(TimSort.java:868)
java.util.TimSort.mergeAt(TimSort.java:485)
java.util.TimSort.mergeForceCollapse(TimSort.java:426)
java.util.TimSort.sort(TimSort.java:223)
java.util.TimSort.sort(TimSort.java:173)
java.util.Arrays.sort(Arrays.java:659)
java.util.Collections.sort(Collections.java:217)
org.jzy3d.plot3d.rendering.ordering.AbstractOrderingStrategy.sort(AbstractOrderingStrategy.java:25)
org.jzy3d.plot3d.rendering.scene.Graph.draw(Graph.java:182)
org.jzy3d.plot3d.rendering.scene.Graph.draw(Graph.java:160)

It is possible to make JDK/JRE7 use the old algorithm by specifying the
following JVM option when starting the program:
-Djava.util.Arrays.useLegacyMergeSort=true

However it looks safer and more portable to adapt the comparator so it does
not violate the contract, like in the follwing:
if (d1d1)
return -1;
else
return 0;

The behavior should be the same, without throwing exceptions when run
on JDK/JRE7.