Atom Feed SITE FEED   ADD TO GOOGLE READER

UniqueList, Bag, and Sorting 2x

I am writing a user interface for data-entry. Because data-entry sucks I am trying to make this UI as usable as possible. This includes accelerator values for each field. For example, a field to enter a car name will include buttons for each of the most popular values for that field. Clicking on such a button will populate the car name with the corresponding value:

Car:
Maxima, 300 Z, Camaro

The list of suggested values is sorted by popularity. The value that has been entered the most times is the first suggestion, and the second most popular entry is second, etc.

Now the fun part is implementing it. Traditionally this would be done with a SQL query with some GROUP BY, ORDER BY and COUNT parameters. Unfortunately I am limiting myself to EJB-QL, which is not (yet) powerful enough to do this.

This got me thinking, could I implement this using Glazed Lists? I want to take an arbitrary EventList and return a second EventList of those values, sorted by popularity.

Since UniqueList has a method to get the count of duplicates for a given value, this is possible. I simply sort the UniqueList by this duplicates count. There's still one major difficulty: UniqueList doesn't currently fire events on that duplicate count changing. But a solution is definitely possible.

Glazed Lists rules.

POI and Hyperlinks

I have a program that generates an Excel file that can contain hundreds of sheets. To make this more manageable, the first sheet is an 'index' sheet that summarizes the sheets to follow. Each row in this index is hyperlinked to the a sheet that provides further detail.

Here's some tips on creating Hyperlinks in Excel .xls documents using Jakarta POI:

  • Excel hyperlinks are formulas with this syntax: HYPERLINK( "target", "label")
  • To link to the first cell in a sheet called sheetname, use "#sheetname!A1"
  • Sheet names in Excel can contain spaces and commas, but you cannot link to such sheets. I recommend sticking to letters, numbers and underscore for best results. Sheet names must also be 31 characters or less. That's 2^5-1.


And finally, a code example:

String targetSheetName = "Sheet3"
String targetCell = "A1";
String targetSheetLabel = "Third Sheet";
String linkFormula = "HYPERLINK(\"#" + targetSheetName + "!" + targetCell + "\", \"" + targetSheetLabel + "\")";

HSSFRow linkRow = ...
HSSFCell linkCell = linkRow.createCell((short)1);
linkCell.setCellFormula(linkFormula);


The result works very well. Now all I gotta do is color the link blue and give it the familar hyperlink underline!

JavaBeans in Comparators

Data objects are the new orange! So I am continuously coding data objects and I find myself writing a lot of repetitive comparison code:
public int compareTo(Object other) {
if(other == null) return -1;
Car otherCar = (Car)other;

if(myColor == null && otherCar.getColor() != null) return -1;
if(myColor.compareTo(otherCar.getColor() != 0) return myColor.compareTo(otherCar.getColor());

if(mySpeed == null && otherCar.getSpeed() != null) return -1;
if(mySpeed.compareTo(otherCar. getSpeed() != 0) return mySpeed.compareTo(otherCar. getSpeed());

if(myYear == null && otherCar.getYear() != null) return -1;
if(myYear.compareTo(otherCar. getYear() != 0) return myYear.compareTo(otherCar. getYear());

return 0;
}


So now I have a new class called Comparators. It has a handy method that gets a custom comparator, given a handful of properties to test in sequence:
private static String[] compareProperties = new String[] { "color", "speed", "year" };
private static Comparator comparator = Comparator.properties(Car.class, compareProperties };
public int compareTo(Object other) {
return comparator.compare(this, other);
}


Reflection and JavaBeans are great! Now all I need to do is extend this so that it also works with equals() and hashCode().

My Bizarre XML Alternative

Originally, the Glazed Lists demo app would parse a file called "issues.xml" each time it was run. I got "issues.xml" by exporting my Issuezilla database from java.net. This worked okay, but the exported XML included everything in the database including attachments. Of course I did not want to include all of this in the download for the demo app.

About six months ago, I exported the xml file from the database and manually removed the attachments with jEdit and grep. This process was labour intensive and yielded only marginal success.

Recently I decided issues.xml file was out-of-date and I needed to bring it up to date. I did not want to fix it manually again.

Conventional thinking would have me create an XSLT transformation or a utility application that took in XML and wrote out trimmed XML. But that strategy is lame.

Instead, I took the parser for the issues.xml file that I already had. This parser read in the issues.xml file to produce an ArrayList of Issue objects. I modified this program so that whenever it modified an Issue object, it System.out.println'd the code that did that:

    public void addFieldAndValue(String currentField, String value) {

if(currentField.equals("issue_id")) {
currentIssue.setId(Integer.valueOf(value));
System.out.println("currentIssue.setId(Integer.valueOf(" + value + "));");
}
...
}


So now my XML parser outputs Java code. I simply compiled this code and now I have a program that produces an ArrayList of issues. But it does no parsing -- it just constructs the issues and returns.

Magic!

Event Listeners and dependencies

There was a recent bug in Glazed Lists that was difficult to plan for. Consider this:
- "S", a source list
- "F1", a filtered transformation of "S"
- "F2", a filtered transformation of "S"
- "FF", a filtered transformation of "F1" that also depends on "F2"

When "S" changes, an event is sent to "F1", which in turn forwards the event to "FF". This returns and then "S" sends the event to "F2". But when "FF" is notified, it inspects "F2", which has not yet received the event from "S". This causes "F2" to be in an inconsistent state, and erratic behaviour ensues. In this case an ArrayIndexOutOfBounds exception was being thrown.

The problem was that although "S" was up-to-date, "FF" was looking at "F2", which was not yet up to date.

The end solution involves explicitly managing the dependencies of each ListEventListener. Now "S" fires an event to "F1", but "F1" does NOT forward it to "FF". Instead, "F1" returns and "S" proceeds to forward the event to "F2". Finally the event is forwarded as if it came from "F1".

Nasty! The code will be in CVS tonight, probably.

IBM developerWorks "TableModel Free" looks too familiar

From ClientJava.com I found a link to TableModel Free, a framework that allows you to "Free yourself from the burden of the TableModel".

TableModel Free reminds me of Glazed Lists:
  • Decorators are used to make the Java Collections observable.
  • It includes a TableModel that observes such a collection to display it in a JTable.
  • It allows the developer to use the same collection for JTable, JList and JComboBox.

    Fortunately, I have nothing to worry about. TableModel Free does not come close to Glazed Lists' functionality:
  • No sorting, filtering or other transformation support
  • No concurrency support
  • No selection support. Add one element to my list, all selection in my JTable is lost
  • No editing support. Tables are view-only.
  • No consideration of performance. When a collection is changed, the entire JTable must be repainted

    And the final problem is that TableModel Free requires you to create XML files for your tables!

    At least today brings some good news. The fine hackers at Id have released Doom 3 on Linux.
  • SQL "is equal to", <>, and NULL

    Time for a complaint about how SQL deals with NULLs.


    CREATE TABLE colas ( brand VARCHAR(32) );
    INSERT INTO colas ( 'Pepsi' );
    INSERT INTO colas VALUES ( 'Coke' );
    SELECT * FROM colas WHERE brand <> NULL;


    The above select statement does not return { 'Pepsi', 'Coke' }. Instead, it returns 0 rows. This is because I should have written:


    SELECT * FROM colas WHERE brand IS NOT NULL;


    Not a big deal, but it makes my code more complicated. Especially since I'm using PreparedStatements.

    All this coding is making me thirsty!

    Checked and unchecked exceptions in EJBs

    EJBs are good. But here's something lame:
  • Throwing an unchecked exception (ie. RuntimeException) causes the current transaction to be rolled back.
  • Throwing a checked exception (ie. a declared exception) does not roll back the current transaction.

    I never throw any exception from my code that does not require a roll back. It seems so awkward.
  • Shiny! Drawing XP icons

    Today I tried for a few hours to get Xandros to burn some DVDs on my new burner. It was grumpy so after two DVD-coasters I threw in the towel and installed Windows XP. It's a decent alternative to Linux but it has a crappy shell and its SMB client sucks.

    While I was waiting I drew up some pretty sort-arrow indicator icons for Glazed Lists. Look at the screenshots to see my handywork. I've wasted a lot of time on drawing icons for all the various look & feels, but the result looks professional.

    In my comparison I have analyzed how JDNC did their JTableHeader. They've done a lot of work on adding anti-aliasing, plus their right-aligned icons look great. Unfortunately they also blew away the platform-specific header backgrounds! This is particularly obvious in the XP theme. Hopefully Sun can talk to Sun and they can get that straightened out.

    Finally I must complain that Swing's UIManager doesn't let you know which look and feel you're using. Try to differentiate between old-skool metal and ocean, or grey-and-grey Windows 95 and lickable Luna in code. It's hard. I've written code that does that and now I wonder if I should make it available online or something.

    Hello, World

    I've been thinking of starting a tech-focused weblog for a long time now. Well, consider this "Hello. World".

    My intention is to talk about Glazed Lists stuff mostly here, and perhaps some other Java stuff. Personal rants will continue to be on swank.ca.