The Comparer



  • Most of the Swing GUI code in the user-side of our Java application predates Java 1.4. In Java (1.)6, JTables (think DataGrid or the like) got a makeover providing in-built sorting and filtering capabilities; pre-(1.)6 coders had to work around this by creating TableModels which would providing sorting and/or filtering. Our coders did the same, albeit poorly IMO. They created Sorter (not SortingTableModel or TableSorter ... just Sorter) which extends AbstractTableModel (good) and wraps a provided TableModel (also good).

    But the rest of it is quite something to behold. 

    Apparently, they were having synchronization trouble with the underlying TableModel -- it would get updated by the owning data source provider on a separate thread and not fire any events notifying listeners that the model had been changed. So they (tried to) deal with it thusly:

    private void checkTable() {
      // see if the model was updated
    

    //fix for exception caused by unsynchronized threads
    for (int x = 0; x < 20000 && this.index.length != this.model.getRowCount(); x++) {
    }

    if (this.index.length != this.model.getRowCount() {
    throw new RuntimeEXception("sorter not informed of a change in the model: "
    + this.index.length
    + " rows in index, "
    + this.model.getRowCount()
    + " rows in table");
    }
    }

    So they traded one exception for another. Super. And in modern JVMs, the whole loop is likely to be optimized out.

    :sigh:

    But my favorite, is this snippet of a method to compare two rows by a given column:

    private TableModel model = ....
    

    .
    .
    .

    private int compareRowsByColumn(int row1, int row2, int column) {

    Object o1;
    Object o2;

    int result;

    // compare the rows
    o1 = model.getValueAt(row1, column);
    o2 = model.getValueAt(row2, column);

    if (( o1 == null) && (o2 == null)) {

    result = 0;
    

    } else if (o1 == null) {

    result = -1;
    

    } else if (o2 == null) {

    result = 1;
    

    } else

    // neither were null
    
    if (o1 instance of java.lang.Number) {
      double d1 = (( Number )o1).doubleValue();    
      double d2 = (( Number )o2).doubleValue();
    
      if (d1 == d2) {
        result = 0;
      } else if (d2 &lt; d1) {
        result = 1;
      } else {
        result = -1;
      }
    

    } else if (o1 instanceof java.lang.Date) {
    Date d1 = (Date) o1;
    Date d2 = (Date) o2;

     result = d1.compareTo(d2);
    

    } else if (o1 instanceof java.lang.Boolean) {
    boolean b1 = ((Boolean)o1).booleanValue();
    boolean b2 = ((Boolean)o2).booleanValue();

     if (b1 == b2) {
       result = 0;
     } else if (b1) {
       result = 1;
     } else {
       result = -1;
     }
    

    } else if (o1 instanceof ComparableType1) {
    ComparableType1 ct1 = (ComparableType1) o1;
    ComparableType1 ct2 = (ComparableType1) o2;

     result = ct1.compareTo(ct2);
    

    } else if (o1 instanceof (ComparableType2) {
    ComparableType2 ct1 = (ComparableType2) o1;
    ComparableType2 ct2 = (ComparableType2) o2;

     result = ct1.compareTo(ct2);
    

    } else {

      .
      . total of 12 ComparableTypes ...
      .
    

    } else {
    String s1 = o1.toString();
    String s2 = o2.toString();

     result = s1.compareTo(s2);
    

    }

    return result;
    }

    Instead of using polymorphism they created this monster (which was added to over the years as various things needed to be displayed in a Sorter-backed JTable. Well, being the clever guy I thought I was, I decided to add the polymorphism bit:

    ...
    

    // neither were null

    if ( o1 instanceof Comparable ) {
    Comparable c1 = (Comparable) o1;
    Comparable c2 = (Comparable) o2;
    result = c1.compareTo(c2);
    } else

    ...

    I tested it with data, it worked, sorted properly and all was hunky dory.

    Until it went into operations.

    As it turns out, someone thought it would be clever to handle the cases where there was no data (apparently null cells are to be avoided at all costs!) and create their own comparable object to stuff into a model cell when the actual value was null so the user would have something nice to look at:

    public class EmptyElement implements 
    

    Comparable {
    public String toString() {
    return "N/A";
    }

    public boolean equals(Object o) {
    try {
    if (this.compareTo(o) == 0) {
    return true;
    }
    } catch (Exception e) {
    }
    return false;
    }

    public int compareTo(Object o) throws ClassCastException {
    if (( o != null ) && ( o instanceof EmptyElement )) {
    return 0;
    } else {
    return 1;
    }
    }
    }

    As you no doubt can guess, this breaks sorting ... some values will be compared by Number, or Date or something that makes sense ... but when a comparison is made against a cell containing EmptyElement, it drops into string-compare mode ... thus, N/A's will be ordered in the list somewhere south of 'middle', on average. This confuses users.

    But worse, it breaks my polymorphic check since it assumes (hah!) that columns are homogenous types ... nulls are caught ahead of my insertion. My new call would create a ClassCastException when stumbling upon an EmptyElement.

    It was easiest just to take out my code.



  • @zelmak said:

    It was easiest just to take out my code.
    Been there, feel your pain.

    Or, you could always try something evil, like:

    result = c1.toString().compareTo(c2.toString());

     



  • But... but... your code was RIGHT!



  • @snoofle said:

    @zelmak said:

    It was easiest just to take out my code.
    Been there, feel your pain.

    Or, you could always try something evil, like:

    result = c1.toString().compareTo(c2.toString());

    Is that not essentially the last branch of the huge if-else-if-else? I'm trying to see something tricksy there ... if there's something tricky about your suggestion, I'm not seeing it ... then again ... I've spent the day trying to untangle Sorter and use "JTable 6" instead ... I don't think its possible without a major re-write.



  • @zelmak said:

    ...

    // neither were null

    if ( o1 instanceof Comparable ) {
    Comparable c1 = (Comparable) o1;
    Comparable c2 = (Comparable) o2;
    result = c1.compareTo(c2);
    } else

    ...


    Surely you can take your code and just add some exception handling for the stupid case?

    ...
      if ( o1 instanceof Comparable ) {
        try {
          result = ((Comparable)o1).compareTo(o2);
        }
        catch (Exception ex) {
          // Suitable special case
        }
      }
    ...


  • @zelmak said:

    Java 1.4. In Java (1.)6
     

    <obligatory derail>

    Just had a thought... what happens after Java 1.7 becomes 1.8 then 1.9...? 

    "No, I know Java 1.4 *is* Java2, but it's not Java 2.0, that came after Java 9...  look - fuck it, we need to upgrade, okay?"



  • @zelmak said:

    As it turns out, someone thought it would be clever to handle the cases where there was no data (apparently null cells are to be avoided at all costs!) and create their own comparable object to stuff into a model cell when the actual value was null so the user would have something nice to look at:

    Consider using a custom TableCellRenderer instead.



  • @Cassidy said:

    Just had a thought... what happens after Java 1.7 becomes 1.8 then 1.9...? 

    "No, I know Java 1.4 *is* Java2, but it's not Java 2.0, that came after Java 9...  look - fuck it, we need to upgrade, okay?"

    Java 11g.

     



  • @Severity One said:

    @Cassidy said:

    Just had a thought... what happens after Java 1.7 becomes 1.8 then 1.9...? 

    "No, I know Java 1.4 is Java2, but it's not Java 2.0, that came after Java 9...  look - fuck it, we need to upgrade, okay?"

    Java 11g.

     

    +1... sadly :-(

    Oh! And nice way of fucking up the whole Comparable idea provided by Java.



  • @ubersoldat said:

    @Severity One said:

    @Cassidy said:

    Just had a thought... what happens after Java 1.7 becomes 1.8 then 1.9...? 

    "No, I know Java 1.4 *is* Java2, but it's not Java 2.0, that came after Java 9...  look - fuck it, we need to upgrade, okay?"

    Java 11g.

     

    +1... sadly :-(

    Oh! And nice way of fucking up the whole Comparable idea provided by Java.

     

    On a related note, one of the improvements slated for Java 8 is the ability to use lambda expressions for, among other things, Comparators.

    (More generally, you'll be able to use a lambda expression anywhere you could use an object instance of any interface that provides a single method - Comparator, ActionListener,  Runnable, and others, as well as user interfaces meeting that requirement. The compiler will detect from context which interface type that lambda fills (taking polymorphism into account), and then magically wrap it up into an object implementing that interface providing the lambda as that single method.)

     



  • @fatbull said:

    @zelmak said:

    As it turns out, someone thought it would be clever to handle the cases where there was no data (apparently null cells are to be avoided at all costs!) and create their own comparable object to stuff into a model cell when the actual value was null so the user would have something nice to look at:

    Consider using a custom TableCellRenderer instead.

    You know that. I know that. Hell, we all know that (or should.) However, the original coders were C/C++ coders who were learning Java while building this system from the ground up under contract. So they made it work.



  • @curtmack said:

    On a related note, one of the improvements slated for Java 8 is the ability to use lambda expressions for, among other things, Comparators.

    (More generally, you'll be able to use a lambda expression anywhere you could use an object instance of any interface that provides a single method - Comparator, ActionListener,  Runnable, and others, as well as user interfaces meeting that requirement. The compiler will detect from context which interface type that lambda fills (taking polymorphism into account), and then magically wrap it up into an object implementing that interface providing the lambda as that single method.)

    Portions of our code have broken when going from Java (1.)6 u13 to Java (1.)6 u17 (latest (1.)6 is u33, I believe) -- we fixed what was obviously broken (i.e., not working at all) ... we haven't tried Java (1.)7 yet.

    Upgrades to infrastructure frighten me.



  • @curtmack said:

    On a related note, one of the improvements slated for Java 8 is the ability to use lambda expressions for, among other things, Comparators.
    Well, let's wait and see. We waited 4.5 years for Java 7, and wen it finally arrived the new features were not exactly earth-shattering. Try-with-resource is nice, although I expect that opening database connections, statements and result sets still remains a mess. Catching of multiple exceptions is nice too, and there's been an overhaul in some APIs.

    Other than that, though...

     


  • I survived the hour long Uno hand

    After Minecraft 1.6 came 1.7, then 1.8, then 1.9... Then Release 1.0. All previous versions were renamed "Beta 1.X", as opposed to "Release 1.X" (they're on 1.4 now if I remember rightly).

    All of which is confusing enough, until you realize that the Xbox version is several version numbers behind. It launched as "Beta 1.6"... and is still not yet at "Release 1.0" despite being downloadable in the XBLA which is, arguable, a "released" state for an Xbox game.



  • @yamikuronue said:

    After Minecraft 1.6 came 1.7, then 1.8, then 1.9... Then Release 1.0. All previous versions were renamed "Beta 1.X", as opposed to "Release 1.X" (they're on 1.4 now if I remember rightly).

    All of which is confusing enough, until you realize that the Xbox version is several version numbers behind. It launched as "Beta 1.6"... and is still not yet at "Release 1.0" despite being downloadable in the XBLA which is, arguable, a "released" state for an Xbox game.

    Minecraft version numbers exist on a continuum that passes through several higher dimensions, so it's understandable that it doesn't make any sense to us third dimensional creatures.



  • @Cassidy said:

    @zelmak said:

    Java 1.4. In Java (1.)6
     

    <obligatory derail>

    Just had a thought... what happens after Java 1.7 becomes 1.8 then 1.9...? 

    "No, I know Java 1.4 is Java2, but it's not Java 2.0, that came after Java 9...  look - fuck it, we need to upgrade, okay?"

    I've always through a version "number" was technically two, the first being a major release and the second number being a minor update (The third being a bug fix, the forth being a build no, in some cases I've seen). So it'll go to Java 1.11. Just to confuse things more.

    Pretty sure Cassidy knew that (I'm one of those people that judges intellect by # of posts), but maybe someone didn't...



  • @Adanine said:

    I'm one of those people that judges intellect by # of posts
     

    Two words.

    The first is "Spectate". You may guess the second.



  • @Cassidy said:

    @Adanine said:

    I'm one of those people that judges intellect by # of posts
     

    Two words.

    The first is "Spectate". You may guess the second.

    If post count gives intellect, swampy should have rolled a mage.


  • ♿ (Parody)

    @Ben L. said:

    If post count gives intellect, swampy should have rolled a mage.

    What makes you think he didn't? Ask him about his rocks some time.



  • @boomzilla said:

    @Ben L. said:
    If post count gives intellect, swampy should have rolled a mage.

    What makes you think he didn't? Ask him about his Ioun stones some time.

    NTUFY



  •  @Sutherlands said:

    @boomzilla said:
    @Ben L. said:
    If post count gives intellect, swampy should have rolled a mage.

    What makes you think he didn't? Ask him about his Ioun stones some time.

    NTUFY

    Ioun stones.



  • @curtmack said:

     @Sutherlands said:

    @boomzilla said:
    @Ben L. said:
    If post count gives intellect, swampy should have rolled a mage.

    What makes you think he didn't? Ask him about his Ioun stones some time.

    NTUFY

    Ioun stones.

    403 Forbidden error.



  • @Sutherlands said:

    @curtmack said:

     @Sutherlands said:

    @boomzilla said:
    @Ben L. said:
    If post count gives intellect, swampy should have rolled a mage.

    What makes you think he didn't? Ask him about his Ioun stones some time.

    NTUFY

    Ioun stones.

    403 Forbidden error.


Log in to reply