Monday, February 18, 2008

Play Safe with Swing

Last month, I was writing a GUI based code in Java. As Swing provides rich UI so I decided to go for Swing. Here is a simple piece of Suggestion, which most of us know already. If you are a novice in Swing, you always need to take care in calling UI work. Reason is simple, swing is not thread-safe and calling GUI work randomly will lead to a deadlock in code. We are seeing 'n' no. of problem with Swing code, just because developers don't know(or maybe forget) that swing is not a thread-safe world. Sometime you are not able to see the problem initially but afterward when your code is going heavier and heavier you will face lot of problems.

How to make Swing Thread-safe ?

Its not a tough job. First see this, this is more or less like a rule and you can apply anywhere to write a safe code:

public static void main(String[] args) {
JFrame frame = new JFrame();
frame.show();
// Anything after this is going to be crappy and thread unsafe
}

If you are going to write two UI work, and since things are not synchronized, you can go into a big mess. I wanted to post my code where I got the problem some days back, but its too big to post. No worries I got a code from here which is amazing ! You know what is cool, it crashes every time :)

import javax.swing.*;
import java.awt.*;

public class StrangeProblem extends JFrame {
static {
new StrangeProblem();
}

private static void staticMethod() {
System.out.println("This is never reached");
}

private StrangeProblem() {
getContentPane().add(new MyJDesktopPane());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 300);
setVisible(true);
// If commented out, program works fine, otherwise it hangs
new JInternalFrame();
}

private class MyJDesktopPane extends JDesktopPane {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("We will now call the static method...");
staticMethod();
System.out.println("Static method was called.");
}
}

public static void main(String[] args) {
}
}

(Thanks for the poster of this code)



So now you got the thumb rule, what not to do ! Alright time to see what to do.

Simple. Run your code in the event-dispatching thread. Most of the UI like event, mouse clicks are always handled by event-dispatching thread. SwingUtilities class provide two methods invokeLater() and invokeAndWait() to get rid of this problem. Now, why two different methods is a big mystery, but use invokeLater() if its a case of Swing. So, now the code skeleton is :

public static void main(String[] args) {

SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame();
f.show();
// you can write more UI work here
}
});
}

I can leave it on to you to make the above code deadlock free :). For more detail, see the SwingUtilities class JavaDoc.

Saturday, February 09, 2008

Sorting with Different Locale

Sorting is always a tricky game in any language. Language like Java has its own high class sorting API's. But have you ever think, how sorting works in different locales ? How it work in French or in Spanish? Lets have a look...

This is my String Array which I want to sort:
String[] names = {"fácil", "facil", "fast","Où", "êtes-vous", "spécifique", "specific", "ou"};
It contains words of French Locale(some of my fav. words like Où :-) )
And here goes our typical sorting program:

String[] names = {"fácil", "facil", "fast","Où", "êtes-vous", "spécifique", "specific", "ou"};
List list = Arrays.asList(names);
Collections.sort(list);
Iterator itr = list.iterator();
while(itr.hasNext()) {
System.out.print(itr.next()+ " ");
}

And the result:
Où facil fast fácil ou specific spécifique êtes-vous

Result can surprise you and can make your French friend angry :-) because he never want "fast" should come before "fácil", just because there is one special 'á' (sorting is true according to UNICODE sequence but not according to locale)

To face this problem JDK comes with something called Collator (I guess in 1.4 onwards) which take care of locale while sorting.
Collator is an abstract class. You can look the source code at location in Openjdk: jdk\src\share\classes\java\text\Collator.java. Highly documented file.

Collator has some flavors like PRIMARY, SECONDARY, TERTIARY, IDENTICAL which all tells what need to take care while sorting. Please read the javadoc for detail.

Now here is my simple code:

import java.text.*;
import java.util.*;


class CollatorTest {

public static void main(String[] args) {
String[] names = {"fácil", "facil", "fast","Où", "êtes-vous", "spécifique", "specific", "ou"};
List list = Arrays.asList(names);
Collections.sort(list);
Iterator itr = list.iterator();
while(itr.hasNext()) {
System.out.print(itr.next()+ " ");
}

Locale[] loc = Collator.getAvailableLocales();

/* for(int i=0;
&<
{
System.out.println(loc[i].getDisplayName());
}
*/
Collator myCollator = Collator.getInstance(new Locale("fr"));
myCollator.setStrength(Collator.PRIMARY);
Collections.sort(list, myCollator);
itr = list.iterator();
System.out.println("");
while(itr.hasNext()) {
System.out.print(itr.next() + " ");
}

myCollator.setStrength(Collator.TERTIARY);
Collections.sort(list, myCollator);
itr = list.iterator();
System.out.println("");
while(itr.hasNext()) {
System.out.print(itr.next() + " ");
}
}
}

And here is the result:
Où facil fast fácil ou specific spécifique êtes-vous
êtes-vous facil fácil fast Où ou specific spécifique
êtes-vous facil fácil fast ou Où specific spécifique

First one is the normal sorting, second and third is Collator sorting with 2 different types. You can very easily see that we are giving respect to other locale as well in sorting. There are 2-3 line comments in the code, which will tell which all locale Collator is supporting.