The Daily WTF: Curious Perversions in Information Technology
Welcome to TDWTF Forums Sign in | Join | Help
in Search

The Comparer

Last post 11-17-2012 12:55 AM by Ben L.. 22 replies.
Page 1 of 1 (23 items)
Sort Posts: Previous Next
  • 11-06-2012 1:00 PM

    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 < 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.

  • 11-06-2012 2:23 PM In reply to

    Re: The Comparer

    zelmak:
    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());

     

    Be brief, no matter how long it takes.
  • 11-06-2012 3:55 PM In reply to

    Re: The Comparer

    But... but... your code was RIGHT!
  • 11-06-2012 4:20 PM In reply to

    Re: The Comparer

    snoofle:

    zelmak:
    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.

  • 11-06-2012 4:30 PM In reply to

    Re: The Comparer

    zelmak:
    ...
    
      // 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
        }
      }
    ...
    You'll probably find that the forum experience is improved by going to the "Site Options" tab of "Edit Profile" and turning off "Display User Signatures".
  • 11-06-2012 4:37 PM In reply to

    Re: The Comparer

    zelmak:
    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?"

  • 11-06-2012 5:14 PM In reply to

    • fatbull
    • Top 500 Contributor
    • Joined on 10-06-2008
    • 
    • Posts 226

    Re: The Comparer

    zelmak:
    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.

    CLI is like a text adventure.
  • 11-07-2012 2:15 AM In reply to

    Re: The Comparer

    Cassidy:
    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.

     

  • 11-07-2012 4:59 AM In reply to

    Re: The Comparer

    Severity One:

    Cassidy:
    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.

  • 11-07-2012 10:53 AM In reply to

    Re: The Comparer

    ubersoldat:
    Severity One:

    Cassidy:
    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.)

     

  • 11-07-2012 2:13 PM In reply to

    Re: The Comparer

    fatbull:

    zelmak:
    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.

  • 11-07-2012 2:17 PM In reply to

    Re: The Comparer

    curtmack:
    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.

  • 11-08-2012 5:29 AM In reply to

    Re: The Comparer

    curtmack:
    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...

     

  • 11-08-2012 7:46 AM In reply to

    Re: The Comparer

    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.
  • 11-09-2012 9:01 AM In reply to

    Re: The Comparer

    yamikuronue:
    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.
  • 11-13-2012 11:52 PM In reply to

    Re: The Comparer

    Cassidy:

    zelmak:
    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...

  • 11-15-2012 3:11 PM In reply to

    Re: The Comparer

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

    Two words.

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

  • 11-15-2012 8:04 PM In reply to

    • Ben L.
    • Top 10 Contributor
    • Joined on 12-22-2010
    • HELP I'M TRAPPED IN A COMMUNITY SERVER FACTORY
    • Posts 3,404

    Re: The Comparer

    Cassidy:

    Adanine:
    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.
    mi'e .ben.ly. .i na selju'o mi la lojban
  • 11-16-2012 5:57 AM In reply to

    Re: The Comparer

    Ben L.:
    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.
    this post is bossy
  • 11-16-2012 9:45 AM In reply to

    Re: The Comparer

    boomzilla:
    Ben L.:
    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
  • 11-16-2012 11:35 AM In reply to

    Re: The Comparer

     

    Sutherlands:
    boomzilla:
    Ben L.:
    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.

  • 11-16-2012 11:17 PM In reply to

    Re: The Comparer

    curtmack:

     

    Sutherlands:
    boomzilla:
    Ben L.:
    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.
  • 11-17-2012 12:55 AM In reply to

    • Ben L.
    • Top 10 Contributor
    • Joined on 12-22-2010
    • HELP I'M TRAPPED IN A COMMUNITY SERVER FACTORY
    • Posts 3,404

    Re: The Comparer

    Sutherlands:
    curtmack:

     

    Sutherlands:
    boomzilla:
    Ben L.:
    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.
    mi'e .ben.ly. .i na selju'o mi la lojban
Page 1 of 1 (23 items)
Powered by Community Server (Non-Commercial Edition), by Telligent Systems