Wednesday, March 21, 2007

Java 5 Conversion Notes

Having finished hackyCore_Kernel, my basic strategy for updating Hackystat code to 1.5 is currently the following:

(0) SVN update, then 'ant -q freshStart all.junit'. Make sure the system isn't busted before you start busting on it. (Be sure to configure the hackystat.build.properties file to include the modules you will be working on.)

(1) Use Eclipse to identify a class containing at least one warning.

(2) Fix instance variables. Navigate to that class, then go to the top of the file and check for any collection classes as instance variables. If present, add type information. For example,
private static TreeMap  numOfPeriodsMap = null;
becomes
private static TreeMap<String, String> numOfPeriodsMap = null;
Note that this often requires some hunting through the file to determine the kinds of objects being added to the collection.

(3) Fix collection references. Continue through source code, updating references to collection classes to include type information. For example,
numOfPeriodsMap = new TreeMap(new StringIntegerComparator());
becomes
numOfPeriodsMap = new TreeMap<String, String>(new StringIntegerComparator());
(4) Update comparators to include type information. For example,
public class StringIntegerComparator implements Comparator {
public int compare(Object o1, Object o2) {
becomes
public class StringIntegerComparator implements Comparator<String> {
public int compare(String o1, String o2) {
(5) Update method signatures to include type information. For example,
public static TreeMap getYearOptions() {
becomes
public static TreeMap<String, String> getYearOptions() {
(6) Remove occurrences of "old style" for loops. For example,
Set analysisNameList = manager.getAnalysisNames();
for (Iterator i = analysisNames.iterator(); i.hasNext();) {
String analysisName = (String) i.next();
String enabled = request.getParameter(analysisName);
becomes
Set<String> analysisNameList = manager.getAnalysisNames();
for (String analysisName : analysisNames) {
String enabled = request.getParameter(analysisName);
In some situations, it doesn't make sense to update them. For example, I've seen loops where the next() method was called twice in each body (the list contained "pairs" of objects that were operated on two at a time.) In this case, you must leave it as an "old style" loop.

(7) Implement Iterable<T> if necessary. In some cases, to accomplish (6) you must have a class implement "Iterable". For example, the SdtManager class should enable you to iterate across all instances of SensorDataTypes using the following for/in loop:
for (SensorDataType sdt : SdtManager.getInstance())
To accomplish that, the SdtManager class had to be changed from:
public class SdtManager  {
:
public Iterator iterator() {
to
public class SdtManager implements Iterable<SensorDataType> {
:
public Iterator<SensorDataType> iterator() {

(8) Remove vestigial casts. After adding in the type information, you will hopefully be able to remove casts.

(9) Remove vestigial imports. When you're all done with a class, you will hopefully need to remove imports. For example:
import java.util.Iterator;

(10) Use @SuppressWarnings for the SerialVersionUID warning. There's just no reason to add this instance variable for Hackystat code; it will not be serialized.

(11) Continue until Eclipse reports no warnings for this class.

Note that sometimes this strategy requires working on several classes at once when they are interdependent.

(12) 'ant -q freshStart all.junit', then SVN commit. Be sure the system is OK, then commit your changes du jour. I found that my Java 5 updates would sometimes create Checkstyle errors, so be sure to do a 'freshStart'. (Double check that the hackystat.build.properties file includes the modules you have been working on.)

Also, I'm cleaning up documentation, removing the @version tag that is a relic of the CVS days, etc. as I work on the class. As long as I'm touching it, I might as well make the JavaDocs better and fix any coding bogosities I encounter.

1 comment:

Sam Joseph said...

Why not migrate to RubyOnRails and save yourself a lot of hassle :-)