Δευτέρα, 17 Δεκεμβρίου 2007

Javapolis 2007

This was my first time in a Javapolis event. Javapolis, like JavaOne, is a fiest of Java. It is not only the new knowledge that one acquires from the various presentations, which are going to be available from parleys.com anyway, but also the exhibition, exchanging ideas with other Java developers, meeting famous authors etc.

This event was even more special because the legend, James Gosling himself, was there! 3000+ participants watched his speech live! Javapolis takes place in Antwerp, Belgium, every year since 2004. It is equal, or even better as some say, from JavaOne, in the US. I really had very good time all these five days, from 10th to 14th of December.

Here is my point of view of the event, the program of which you can find here. Unfortunately, there had been 5 parallel sessions so if you wished to attend more than one of them at the same time, then you had a problem.

Day 1 (University day)

I chose to start my day with Maurice Naftalin's “Java Generics and Collections” speech. Maurice is the author with an excellent book with the same title. He presented a deep understanding on Generics issues, what to do and what to avoid, e.g. problems like casts and instanceof, parametric exceptions and problems with arrays. “Never ignore unchecked warnings” he said. Generics may have not had the great acceptance in the Java community yet, but I believe it is one of the positive additions of the language, and if you learn how to use them correctly, you can get great benefits from them.

After the break, I didn't watch the 2nd part on Collections, as I also wanted to watch Sang Shin's “Introduction to Ajax”. Another excellent speaker, Sang maintains the Javapassion web site where he gives lessons in Java for free! If you check his site you 'll see a lot of information in almost every aspect of the language, and all this for free! Sang gave an overview of the main Ajax toolkits like Dojo, GWT, jMaki, JSF 2.0 with Ajax support etc. Amazing to watch this guy on stage.

After the lunch break, I attended “Seam in Action”. Seam is an extention to JSF, and as the speakers said, Seam is the new Struts. E.g. apart from Event, Session and Application, Seam also introduces the notions of Page, Conversation and Business Process. It seems to be a good extension and improvement upon JSF.

In the afternoon, I attended "BPM in action" using SoftwareAG's webMethods software, another tool for Business Process Modelling development. Unfortunately, the demo didn't work and so wasn't demonstrated by the good though speaker, Hilde Janssen.

Finally, I attended JetBrains' "Teamcity" tool. It seems to be an excellent tool for teamworking. Mike Aizatsky, an excellent speaker, demonstrated, using simple and to the point slides, the benefits of using the tool, which is now in version 3 and you can download it for free. E.g. one use of the tool is before the Version Control system, where it checks whether your code compiles before submitting it to version control. This way, you can be sure that only compiled versions are submitted to version control.

Day 2 (University day)

In day 2, the exhibition kiosks were set in place. The conference sponsors, like SUN, Adobe, Jboss, Intellij, Oracle, IBM and others, provided information about their products, and gave gifts to the participants. The participants could also participate in many video games and competitions. For example, the faster driver of a BMW emulator, brought by Cegeka, gained a PSP each day. JavaBlackBelt also had daily competitions; you had to answer 5 tricky multiple choice questions in a limited time. Those who had the best score and finished earlier participated in the finals for a PSP 3. And of course, Adobe, gave away O' Reilly books on Flex 2 and ActionScript. And there were many more gifts from other sponsors. This is the best part of Javapolis I think!

I began my second day with Bruce Eckel's presentation “Thinking in Flex”. Bruce moved to Adobe for those who didn't know. It was great to watch the author of one of the best Java books “Thinking in Java” live on the stage. Along with the help of Christophe Rooms, they presented Adobe Flex. It was a very good presentation and Flex 2 is a very impressive tool that now works with Java as the back end. I wish Java had the same impressive GUI components as Adobe Flex does. I avoided developing in Flash all these years because, however impressive the result of a Flash application is, the code is difficult to maintain. But with Flex and ActionScript you can create impressive Flash applications with a powerful scripting language that is easy to maintain. Even though a proprietary product, I 'll definitely give it a try. The new version of parleys.com is going to be built with this tool!

“JavaFX in Action” by Jim Weaver was my next attendance. I don't know if you have noticed, but there is a plethora or scripting languages nowadays and many more are developed. See e.g. Perl, PHP, Ruby, JavaScript, ActionScript, Wicket just to name a few. And many developers prefer to develop faster using a scripting languages. For many, scripting languages are the future of programming. Java's answer is JavaFX, a scripting language based in Java that allows you to develop quickly from swing applications for desktops, to mobile, or even web applications. SUN seems to have put a lot of effort in this new language, so I 'll give it a try and I suggest you do too.

In the afternoon I watched “Developing software like a band plays Jazz” presented by another legend, Erich Gamma, one of Gang of Four guys. Now employed by IBM, he presented Jazz, a collaboration product for enterprises. Erich addressed one of the main problems in companies today, lack of collaboration, which this tool addresses. Even though I didn't like the presentation, this tool seems to be of great help to software teams. And, it is an IBM product.

Day 3 (Conference day)

The day started with the speech of the father of Java, James Gosling, after a welcome by the event's responsible, Stephan Janssen, president of BeJUG, and an uncoference kick-off by Bruce Eckel. Three conference rooms where full just to watch the legend. The father of Java shared impressive statistics with the audience: 5 million Java enabled devices worldwide, 6 million Java developers, 12 million JRE downloads per week, 4 million Netbeans and 2.5 million Glassfish downloads. “If you are still using Emacs, then go shoot yourself”, said James, announcing Netbeans version 6.0. This new version has support for not only Java but also for Ruby, C++, Javascript and PHP. And as I say to my friends who use Visual Studio, just go and download Netbeans to see how Visual Studio 2012 is going to be like! James, also showed some impressive statistics regarding the speed of Java 6.0 which is now equal in speed or even outperforms C++! Far behind is C#, which is still very slow in all benchmarks. Finally, the father of Java asked the community to download and try JavaFX, where SUN puts a lot of effort in. We watched a couple of applications on Java, such as a very impressive demo of navigating two robots!

After this impressive beginning, the rest of the day continued with a presentation of “SoapUI”, a very good commercial product to test SOA applications, something that was actually missing from SOA apps.

“Google Web Toolkit” gave a nice overview of GWT also through the presentation of OpenSocial, an application developed by GWT.

The afternoon sessions were far more interesting. Instead of “Scrum in practice for non-believers”, Sander Hoogendoom gave a presentation on Anti-management practices or how to make your project fail! An excellent presentation that shows the reality in most software projects, and what to avoid to make your project succeed, or rather fail, in the speaker's words. He successfully presented common syndromes encountered today in project managers, architects, developers etc. The presentaiton is live on parleys.

The last presentation of the day was actually a Q&A with James Gosling, Joshua Bloch, Neal Gafter and Martin Odersky were the speakers answered questions posed by the audience. Not very successful in my opinion though.

Day 4 (Conference day)

The 2nd conference day also started dynamically with a presentation of Adobe Flex by Bruce Eckel, followed by a speech of the Javapolis' creator, Stephan Janssen, who presented parleys.com version 2, built on Adobe Flex, and a keynote on JavaFX by Tim Cramer. We had the chance to see the possibilities of Netbeans 6 in developing mobile applications. Did you know that JME now comes with a games engine for mobile game applications?

After the break, I watched JPA 2.0 by Linda Demichiel, the chief architect of JPA at SUN Microsystems. With 20 years experience in IT, she gave an overview of JPA's new features and future work on it.

After the lunch break, the day was devoted to Joshua Bloch, an excellent speaker and author! The author of the most popular Java book “Effective Java”, changed his initial presentation and gave a presentation on Closures, a feature to be added to Java 7. Closures will allow to pass whole piece of code from one method to another. The speaker presented his skeptisism about this new feature and said that the language has already become too complicated to add yet another such complicated feature. And I wouldn't agree more with him. How many developers actually use most of Java 5's new features? Many still have problems to even understand Generics. Personally, I don't find Closures to be a useful addition to the language. We developers need the language to help us in solving our business or scientific problems in a simple and easy way, and I cannot see how Closures can help us in that. I don't see why to add a feature that will be used rarely or not at all by most of us. I would prefer to see improvements in the existing language syntax, like simplification or best practices of use of the features of the language, properties, even better performance etc.

Emmanuel Bernard gave next a presentation on “Hibernate search” or how to use Hibernate with Lucene to do efficient and effective full text searching.

Mark Janssen, author of “SOA using Java Web Services” book, gave a technical overview of how to develop web services with Java.

The day had the best ending ever with “The Java puzzlers” show by Joshua Bloch and Neal Gafter. Dressed with uniforms from Star Wars, they started their show by “fencing” with their “light pointers”! Each one of them posed alternatively 4 Java puzzlers to the other, and the audience had to answer which one of the 4 multiple answers was correct. These were new puzzlers to be added to “Java Puzzlers” version 2 book. If you haven't got this excellent book yet, what are you waiting for? Many participants bought it with 20% discount from the kiosk in the exhibition hall after this presentation!

This was the best day of the conference, ended with many more gifts, and competitions by the sponsor companies, as this was the last day of the exhibition. The day finshed by watching the movie “BeoWulf”.

Day 5 (Conference day)

The last day of the conference lasted only half a day and was not as good as the previous ones. “Wicket2”, presented by Martjin Dashorst, seems to be another very cool framework for building web applications using simply Wicket scripting language inside HTML for the presentation layer and Java for the business logic. No complicated things like JEE and EJB. Very promising framework which I 'll give it a try.

The rest presentations of the day were not that interesting. I started with OSGi, but I found the presentation rather boring and then left to watch “Innovating with Java” which I didn't find interesting either.

“Testing Driven Development” by the author Lasse Koskela of the book with the same name as the presentation, didn't say many new things to what I already know.

Thus, the conference finished.

A few final comments. The food was awful. An event, or something could also have been organised the last day of the conference to give us a better feeling of the last day and keep up our interest. Free drinks, like coke, sprite, water, even beer, were offered all five days. The exhibition was great!

You can watch all presentations from parleys.com when they become available. But of course, the value is to attend the conference, to live the excitement of these five days, to meet new people with same interests, to participate in the competitions and games, to get all these gifts, T-shirts, Cds, coffee cups, pens, bags etc. This is the spirit of Javapolis. See you there next year.

Πέμπτη, 11 Οκτωβρίου 2007

Greek and Java

Under this subject I publish any issues that a Java programmer may encounter with Greek letters while writing Java programs. This information might be useful to programmers of other native languages too.

Issue 1: Apache Tomcat doesn't display Greek

Instead ???? are displayed.

Solution
You have to perform the following steps:
  1. Edit conf/server.xml like so:

    connector port="8080" maxhttpheader="8192" maxthreads="150" minsparethreads="25" maxsparethreads="75" enablelookups="false" redirectport="8443" acceptcount="100" connectiontimeout="20000" disableuploadtimeout="true" URIEncoding="ISO-8859-7" or URIEncoding="UTF-8" depending on your application.

  2. If you are using a servlet, then you have to add the following lines of code to your doGet() method:

    request.setCharacterEncoding("ISO-8859-7"); // or UTF-8
    response.setCharacterEncoding("ISO-8859-7"); // or UTF-8


  3. If you are using a JSP, then you must include the following header on the beginning of the jsp page:

    <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> // or ISO-8859-7
  4. You may need to add this line too
    // or ISO-8859-7
  5. In case the encoding of the GET command is different than the one in URIEncoding (e.g. ISO-8859-7 and URIEncoding="UTF-8"), then use the command request.getQueryString() to get the original query string, before this is changed by Tomcat's URIEncoding, manually parse the string to a variable, e.g. myVar, and then use e.g. URLDecoder.decode(myvar, "ISO-8859-7").

Note: This info applies to JBoss too, as it is based on Tomcat.

References:
  1. JHUG forum link
  2. Apache Tomcat wiki
  3. The absolute minimum about Unicode and Character Sets

Issue 2: Greek support in Eclipse IDE

Right click an eclipse project and select Properties. In the info property, under Text file encoding box, select one of the following encodings:
  • ISO-8859-7
  • Cp1253
  • UTF-8
Issue 3: Greek support in Netbeans IDE

Right click on a project and select Properties. In the Encoding property, select one of the following encodings:
  • ISO-8859-7
  • Windows-1253
  • UTF-8
Issue 4: Greek support in MySQL

Use one of the following collations: utf8_unicode_ci or utf8_general_ci. Charset should be UTF-8.
If you are using phpmyadmin, you can do this while creating the database. If you have already created the database, you can change its collation by clicking on Operations tab.
If you can still not see Greek, edit my.conf and under [mysqld] tag add the string
character-set-server = utf8.

To use together with Tomcat:

url="jdbc:mysql://localhost/timesheet?useUnicode=true;characterEncoding=utf8" // or ISO-8859-7

Issue 5: Define encoding

Whenever you open/save a text file, specify the character encoding and don't rely on the OS default encoding:

Reader r = new InputStreamReader(new FileInputStream(file), "UTF-8"); // ISO-8859-7
Reader r = new InputStreamWriter(new FileInputStream(file), Charset.forName("ISO-8859-7"));
Writer w = new OutputStreamWriter(new FileOutputStream(file), "ISO-8859-7"); // UTF-8
Writer w = new OutputStreamWriter(new FileOutputStream(file), Charset.forName("UTF-8"));
String s = new String(byteArray, "UTF-8");
byte[] a = string.getBytes("UTF-8"); // Cp1253

Issue 6: Greek locale
private static final Locale GREEK_LOCALE = new Locale("el", "GR");

Issue 7: Greek and JEditorPane
Unfortunately, JEditorPane supports HTML 3.2 (to some arguable extend), while named Greek entities were added in HTML 4.0. This means, that

jEditorPane.setContentType("text/html");
jEditorPane.setText("αβγδ");
System.out.println(jEditorPane.getText());

will not return the Greek characters but something like

#965;#966;#967;#968

You can try

jEditorPane.setContentType("text/html;charset=utf-8");
jEditorPane.setText(new String("αβγδ".getBytes("utf-8"),"utf-8");

but it still doesn't work.

A solution is simply to use

jEditorPane.setContentType("text/plain");

In any case, it is better to handle the text from the variable it is stored than getting it from jEditorPane.getText().

References:
  1. Java Anti-Patterns


Τετάρτη, 26 Σεπτεμβρίου 2007

Simple suggestions for 'team' programming

Most programmers think that they are unique, that the way they program is the best, and they program only for themselves. This selfish attitude, however, does more bad than good. Working as a team has more advantages, less individual work, better cooperation to achieve deadlines, just to name a few, so why don't we (developers) learn how to cooperate?
Here I put four suggestions, which are so obvious to me, but I don't know why most programmers don't wish to follow:
  1. Use and apply design patterns; they help better communication between the team as the provide for a common language; not to mention all the other advantages they provide, like extensible code, program to an interface etc.;
  2. Don't hesitate to write javadoc and comments; they save another developer that looks at your methods and classes much time to know what it does than having to go through the code to realise what an uncommented method does;
  3. Write test cases; they are mainly for documentation; they show another developer what is the input to a method and what to expect as an output than having to guess each time, especially when there is no javadoc;
  4. Use this *bloody* CVS/SVN of yours correctly; add a comment each time you do a modification letting others know what you did and why. E.g. another guy just erased (or moved to another eclipse project) some classes in the team I work, and he never writes anything in CVS, letting me in nowhere of knowing why he did that and he 's too busy to ask.
If you have never heard of design patterns, then here are some references you may find useful:
Write javadoc comments even to package or private methods explaining the algorithm that you use, any reference to a web site or book, and of course reference back to the use case or requirement that forced you to write this method. In eclipse it is also very easy to produce the html API by clicking on the project in package explorer and then to menu Project --> Generate Javadoc.

Creating junit test cases is also very easy with eclipse, just right click on the class you wish to test and choose New --> JUnit Test Case. Eclipse will ask you of the methods that you wish to test and will create the skeleton for you.

Next time you write a piece of code, think that you don't write it for yourself, but also for others who might review it or continue from where you left or need to modify it. Stop thinking selfishly; it doesn't help.

Heinz Kabutz also gave an interview on becoming a Better Programmer in SUN's website.

Πέμπτη, 13 Σεπτεμβρίου 2007

Secrets of Concurrency (Part 1) -- The Law of the Ritalin Child

Do you read Heinz Kabutz's newsletter? I do, even though I don't always understand what a Java Champion such as Heinz means. :-) As I 'm not a Java champion and I don't think I 'll ever be, I sometimes spend quite a lot of time to follow Java champions' newsletters, speeches etc. :-)
This happened with his issue 146, "Secrets of Concurrency (Part 1) -- The Law of the Ritalin Child". To understand this newsletter, on a difficult topic such as threads, I had to create some drawings on a piece of paper. So, for all the rest of you who didn't understand exactly what that issue says, or who didn't bother to look more seriously into it, here I put a drawing and my understanding of it.

So, back to our subject. The first question Heinz poses in his newsletter is what does InterruptedException mean?
Let's first examine when an InterruptedException is thrown. A thread can be in any one of the states that are shown in the following state transition diagram, i.e. NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING and TERMINATED. See java.lang.Thread.State if you don't believe me.



Now, assume that our thread, lets call it thisThread, is in the RUNNABLE state. Heinz says that
"[i]f the thread is not in a waiting state, then only the interrupted status is set, nothing else."
I.e., if thisThread's state is RUNNABLE, and it calls its interrupt() method, the only thing that happens is that thisThread.interrupted=true. In other words:

// thisThread is in RUNNABLE state
// when
thisThread.interrupt();


then the following occurs:

thisThread.interrupted=true;

"If the thread is currently in either the WAITING or TIMED_WAITING states, it immediately causes an InterruptedException and returns from the method that caused the waiting state."
I.e., if thisThread's state is either WAITING or TIMED_WAITING, and its interrupt() method is called, it throws an InterruptedException and returns from the method that caused the waiting state. Its interrupted status should already be true. However, as we shall see later, when an InterruptedException is thrown, the interrupted status of the thread is flipped back to false. In other words:

// thisThread is in WAITING or TIMED_WAITING state
// when
thisThread.interrupt();


then an InterruptedException is thrown and
thisThread.interrupted=false;

"However, if later on the thread calls a method that would change the state into WAITING or TIMED_WAITING, the InterruptedException is immediately caused and the method returns."
I.e. if thisThread.interrupted=true, because its interrupt() method was called previously, and thisThread called a method that causes a state transition to one of either WAITING or TIMED_WAITING states, then, because thisThread.interrupted is already true, an InterruptedException is immediately thrown and its state remains to RUNNABLE. In other words:

// thisThread is in RUNNABLE state
thisThread.interrupted=true;
// when
thisThread.wait();

then

thisThread.State=RUNNABLE
an InterruptedException is thrown and
thisThread.interrupted=false;

But how can thisThread change its state to WAITING or TIMED_WAITING? By calling one of the following methods: wait(), Thread.sleep(), BlockingQueue.get(), Semaphore.acquire() and Thread.join().

E.g.
try {
Thread.sleep(1000); // 1 second
} catch(InterruptedException ex) {
// ignore - won't happen
}


"Note that attempting to lock on a monitor with synchronized puts the thread in BLOCKED state, not in WAITING nor TIMED_WAITING. Interrupting a thread that is blocked will do nothing except set the interrupted status to true. You cannot stop a thread from being blocked by interrupting it. Calling the stop() method similarly has no effect when a thread is blocked. "
OK? Did you get that? If thisThread.State=BLOCKED, calling its interrupt() method, simply causes thisThread.interrupted=true while calling its stop() method has no effect.

The interrupted status is nowadays commonly used to indicate when a thread should be shut down because it is thrown at well defined places that wouldn't cause any problems, not like Thread.stop() method. The problem with the Thread.stop() method was that it would cause an asynchronous exception at any point of the thread's execution code.

"Note that the interrupted status is set to false when an InterruptedException is caused or when the Thread.interrupted() method is explicitly called. Thus, when we catch an InterruptedException, we need to remember that the thread is now not interrupted anymore! In order to have orderly shutdown of the thread, we should keep the thread set to "interrupted"."

So, what should we do when we call code that may cause an InterruptedException? Heinz has two answers on this:
  1. Rethrow the InterruptedException from your method.
  2. Catch it, set interrupted status, return
E.g.
while (!Thread.currentThread().isInterrupted()) {
// do something
try {
TimeUnit.SECONDS.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
Remember! Don't just ignore interruptions, deal with the cause!

So, to recap:
  • if a thread is in RUNNABLE state and its interrupt() method is called, its interrupted status changes to true;
  • if a thread is in WAITING or TIMED_WAITING state and its interrupt() method is called, it throws an InterruptedException and its interrupted status changes back to false;
  • if a thread is in RUNNABLE state and one of its methods that changes its state to WAITING or TIMED_WAITING is called, and, previously its interrupted status has been set to true by a call to its interrupt() method, then an InterruptedException is thrown, its state changes to RUNNABLE and its interrupted status changes back to false;
  • finally, if a thread is in BLOCKED state and its interrupt() method is called, its interrupted status changes to true; while nothing seems to happen when its stop() method is called. (It seems that if there is a lot of contention for locks, the stop signals get lost).
Actually, for the last case, this can be checked by the following piece of code (contributed by Heinz):

public class CheckBlocking {

public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
public void run() {
while (true) {
System.out.println("t1");
synchronized (CheckBlocking.class) {
try {
CheckBlocking.class.wait(10000);
} catch (InterruptedException e) {
interrupt();
return;
}
}
}
}
};
Thread t2 = new Thread() {
public void run() {
while (true) {
System.out.println("t2 blocked");
synchronized (CheckBlocking.class) {
System.out.println("t2");
try {
CheckBlocking.class.wait(950);
} catch (InterruptedException e) {
interrupt();
return;
}
}
}
}
};

t1.start();
Thread.sleep(2000);
t2.start();
Thread.sleep(2000);
t2.stop();
}
}

If you run this code, you will notice that no exception is thrown. It gets lost somewhere.

Thanks Heinz.

For those of you who want to learn about Java threads, I would recommend "Java concurrency in practice" by Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea, the only book in my opinion that explains in plain english and allows you to understand what is going on in multithreading java and the strange behaviour of your multithreading programs, if you have encountered such.

Τετάρτη, 12 Σεπτεμβρίου 2007

java.util.Formatter

Since JDK 1.5, java has java.util.Formatter class, an interpreter for printf-style format strings. However, the API for java.util.Formatter is rather obscure to understand. This blog is to help you see each format command and its result and be able to use the most appropriate format string for your case right away, without the need to go through the long Java API for this class.

The following info applies to:
  • Formatter.format()
  • String.format()
  • java.io.PrintStream.format()
  • java.io.PrintWriter.format()
  • printf()
but String.format() is only shown here for convenience. The same arguments can be used for the other 2 methods.

Here is a test case to display the use of format strings for date/time. I use this date as an example: 07-01-2007 22:05:01 (7th of January 2007, my name day!)

import java.text.SimpleDateFormat;
import java.util.Calendar;

import junit.framework.TestCase;

/**
* Class to test java.util.Formatter
* @author jkost
* @see java.util.Formatter
* @since JDK 1.5
*/
public class FormatterTester extends TestCase {
private final SimpleDateFormat sf = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
private Calendar cal = Calendar.getInstance();

protected void setUp() throws Exception {
super.setUp();
cal.setTime(sf.parse("07-01-2007 22:05:01"));
}
/**
* Conversion characters for formatting dates.
*
*/
public void testDateFormatter() {
// full day of week
assertEquals("Sunday", String.format("%tA", cal));
// abbreviated day of week
assertEquals("Sun", String.format("%ta", cal));
// full month name
assertEquals("January", String.format("%tB", cal));
// abbreviated month name
assertEquals("Jan", String.format("%tb", cal));
assertEquals("Jan", String.format("%th", cal));
// the century, 00 to 99, i.e. year divided by 100
assertEquals("20", String.format("%tC", cal));
// day of month, 2-digit format
assertEquals("07", String.format("%td", cal));
// day of month, 1-digit format, i.e. without leading zeros
assertEquals("7", String.format("%te", cal));
// Date formatted as "m/d/y" (short numeric form)
assertEquals("01/07/07", String.format("%tD", cal));
// ISO 8601 complete date formatted as "Y-m-d".
assertEquals("2007-01-07", String.format("%tF", cal));
// 3-digit day of year
assertEquals("007", String.format("%tj", cal));
// 2-digit, month of year
assertEquals("01", String.format("%tm", cal));
// no week of year; SUN needs to implement this
// assertEquals("01", String.format("%tw", cal));
// 4-digit year
assertEquals("2007", String.format("%tY", cal));
// last 2 digits of year
assertEquals("07", String.format("%ty", cal));
}

/**
* Conversion characters for formatting times.
*
*/
public void testTimeFormatter() {
// 24-hour of day, 2-digit format, 00-23
assertEquals("22", String.format("%tH", cal));
// 12-hour of day, 2-digit format, 00-12
assertEquals("10", String.format("%tI", cal));
// 24-hour of day, 1-digit format, 0-23
assertEquals("22", String.format("%tk", cal));
// 12-hour of day, 1-digit format, 0-12
assertEquals("10", String.format("%tl", cal));
// minutes, 2-digit format, 00-59
assertEquals("05", String.format("%tM", cal));
// seconds, 2-digit format, 00-59
assertEquals("01", String.format("%tS", cal));
// milliseconds, 3-digit format, 000-999
assertEquals("000", String.format("%tL", cal));
// nanoseconds, 9-digit format, 000000000 - 999999999
assertEquals("000000000", String.format("%tN", cal));
// am or pm
assertEquals("pm", String.format("%tp", cal));
// AM or PM
assertEquals("PM", String.format("%Tp", cal));
// RFC 822 style numeric time zone offset from GMT, e.g. -0800.
assertEquals("+0100", String.format("%tz", cal));
// the abbreviation for the time zone
assertEquals("CET", String.format("%tZ", cal));
// Seconds since the beginning of the epoch starting at 1 January 1970 00:00:00 UTC,
// i.e. Long.MIN_VALUE/1000 to Long.MAX_VALUE/1000.
assertEquals("1168203901", String.format("%ts", cal));
// Milliseconds since the beginning of the epoch starting at 1 January 1970 00:00:00 UTC,
// i.e. Long.MIN_VALUE to Long.MAX_VALUE.
assertEquals("1168203901000", String.format("%tQ", cal));
// Time formatted for the 24-hour clock as "%tH:%tM"
assertEquals("22:05", String.format("%tR", cal));
// Time formatted for the 24-hour clock as "HH:MM:SS".
assertEquals("22:05:01", String.format("%tT", cal));
// Time formatted for the 12-hour clock as "hh:mm:ss AM/PM"
assertEquals("10:05:01 PM", String.format("%tr", cal));
}

/**
* Conversion characters for formatting date/time compositions.
*
*/
public void testDateTimeFormatter() {
// Date and time formatted as e.g. "Sun Jul 20 16:17:00 EDT 1969".
assertEquals("Sun Jan 07 22:05:01 CET 2007", String.format("%tc", cal));
}

/**
* Formats the value as either "true" or "false" (or "TRUE" or "FALSE", for %B).
* For boolean values, this works as expected.
* For all other values, any non-null value is "true", while null values are "false".
* %type
* type = b | B | s | S
*
*/
public void testBooleanFormatter() {
final boolean flag = true;
// boolean
assertEquals("true", String.format("%b", flag));
// boolean
assertEquals("TRUE", String.format("%B", flag));

// as string
assertEquals("true", String.format("%s", flag));
assertEquals("TRUE", String.format("%S", flag));

assertEquals("false", String.format("%b", null));
assertEquals("true", String.format("%b", 1));
assertEquals("true", String.format("%b", 'a'));
}

/**
* Formats the value supplied as a single character.
* Supplied value must be a Byte, Short, Character, or Integer.
* %[argument][width]type
* argument = <
* width = any positive integer value
* type = c | C | s | S
*
*/
public void testCharacterFormatter() {
final char c = 'a';
// character
assertEquals("a", String.format("%c", c));
// character
assertEquals("A", String.format("%C", c));
// as string
assertEquals("a", String.format("%s", c));
assertEquals("A", String.format("%S", c));
}

/**
* %[argument][flags][width]type
* argument = <
* flags = - | + | # | | ( | 0
* width = any positive integer value
* type = d | o | x | X | H | s
* Arguments must be Byte, Short, Integer, Long, or BigInteger.
*/
public void testIntegerFormatter() {
final int intNumber = 7;

// base-10 integer
assertEquals("7", String.format("%d", intNumber));

// base-8 (octal) integer
assertEquals("16", String.format("%o", 2*intNumber)); // 2*7 = 14

// flag '#' indicates that formatted output should appear in alternate form.
// For %o, this means a leading 0.
assertEquals("07", String.format("%#o", intNumber));

// base-16 (hexadecimal) integer
assertEquals("e", String.format("%x", 2*intNumber)); // 2*7 = 14
assertEquals("E", String.format("%X", 2*intNumber)); // 2*7 = 14

// flag '#' indicates that formatted output should appear in alternate form.
// For %x and %X, output will include a leading 0x (0X).
assertEquals("0x7", String.format("%#x", intNumber));
assertEquals("0X7", String.format("%#X", intNumber));

// hexadecimal representation of the value's hashcode
assertEquals("e", String.format("%h", 2*intNumber)); // 2*7 = 14
assertEquals("E", String.format("%H", 2*intNumber)); // 2*7 = 14

// as string
assertEquals("7", String.format("%s", intNumber));

// width 3
assertEquals(" 7", String.format("%3d", intNumber));

// flag '-' means left justification
assertEquals("7 ", String.format("%-3d", intNumber));

// flag '+' indicates that numeric output should always include a sign (+ or -).
assertEquals("+7", String.format("%+d", intNumber));

// flag '(' indicates that negative numbers should appear in parentheses.
assertEquals("(7)", String.format("%(d", -intNumber));

// flag '0' indicates that numeric values should be padded on the left.
assertEquals("007", String.format("%03d", intNumber));

// flag ' ' (space) indicates that non-negative values should be prefixed with a space
// (for alignment with negative numbers).
assertEquals(" 7", String.format("% d", intNumber));

// flag '#' indicates that formatted output should appear in alternate form.
// For %s and %S, the flag is passed on to the object's formatTo( ) method.
// The # flag only works in conjunction with %s and %S if the argument
// implements java.util.Formattable.
assertEquals("7", String.format("%#s", intNumber));

// argument < indicates that the previous argument should be used (again),
// rather than continuing on
assertEquals("+7 7", String.format("%+d %
<3d", , intNumber));
}

/**
* %[argument][flags][width][.precision]type
* argument =
<
* flags = - | + | # | | ( | 0 | ,
* width = any positive integer value
* precision = any positive integer value
* type = f | a | g | G | e | E | h | H | s
* Arguments must be Float, Double, or BigDecimal.
*/
public void testDoubleFormatter() {
final double doubleNumber = 7.0d;

// double/float number, 6 decimal digits by default
assertEquals("7.000000", String.format("%f", doubleNumber));

// double/float number, 5 max decimal digits
// precision is the total number of significant digits to be displayed
assertEquals("7.00000", String.format("%g", doubleNumber));
assertEquals("7.00000", String.format("%G", doubleNumber));

// exponential notation, default number of decimal digits 6
assertEquals("7.000000e+00", String.format("%e", doubleNumber));
assertEquals("7.000000E+00", String.format("%E", doubleNumber));

// exponential notation, using base-16 for the decimal part,
// and base-10 for the exponent part
assertEquals("0x1.cp2", String.format("%a", doubleNumber));

// hexadecimal representation of the value's hashcode
// precision determines the maximum characters output
assertEquals("401c0000", String.format("%h", doubleNumber));
assertEquals("401C0000", String.format("%H", doubleNumber));

// as string
// precision determines the maximum characters output
assertEquals("7.0", String.format("%s", doubleNumber));

// 2 decimal digits (precision = 2)
assertEquals("7.00", String.format("%.2f", doubleNumber));

// width 6, precision = 2 (2 decimal digits)
assertEquals(" 7.00", String.format("%6.2f", doubleNumber));

// flag '-' means left justification
assertEquals("7.00 ", String.format("%-6.2f", doubleNumber));

// flag '+' indicates that numeric output should always include a sign (+ or -).
assertEquals(" +7.00", String.format("%+6.2f", doubleNumber));

// flag '(' indicates that negative numbers should appear in parentheses.
assertEquals("(7.00)", String.format("%(.2f", -doubleNumber));

// flag '0' indicates that numeric values should be padded on the left.
assertEquals("007.00", String.format("%06.2f", doubleNumber));

// flag ',' indicates that the locale-specific grouping character
// should be used for numeric values.
assertEquals("7,00", String.format(new Locale("el"), "%,.2f", doubleNumber));

// flag ' ' (space) indicates that non-negative values should be prefixed with a space
// (for alignment with negative numbers).
assertEquals(" 7.00", String.format("% .2f", doubleNumber));


// argument < indicates that the previous argument should be used (again),
// rather than continuing on
assertEquals("7.00 7.0", String.format("%.2f %
<3.1f", doubleNumber));
}

public void
testIntegerNumberFormatter() {
final double
intNumber = 7;

// another way to format integers
NumberFormat nf = NumberFormat.getInstance();
nf.setMinimumIntegerDigits(3);
nf.setParseIntegerOnly(true);
assertEquals("007", nf.format(intNumber));
}
}