JPList

Developer Notes: Conversion to Java 1.5

Top

Overview

The JPList library was originally (version 1.0) built using Java 1.4. Currently (since version 1.1), JPList is available for both Java 1.4 and Java 1.5. This document lists the rationale and goals for the conversion, the list of changes that were made to take advantage of Java 1.5's new features, and the benefits that accrue to the user as a result.

Top

Goals

The rationale and goals for the conversion of JPList to use Java 1.5 are listed below. In the following section, we describe the design decisions and other changes that ensured that each of these goals were met.

Top

Adding type safety through enums

The Java 1.5 version adds a new Enumerated Type class in the net.sf.jplist.core package. Each element of the enum corresponds to a different PList data type. In the Java 1.4 version, this was done using constants in the net.sf.jplist.core.IElement interface.

A new method IElement.getType()::Type has been added, which each implementation is required to implement. It returns the correct Type enumeration element for that data type. Clients of the Java 1.5 version are advised to use this method instead of the IElement.typeOf()::int method.

Code comparison links:

Top

Adding Type safety through generics

In order to provide a simple unified API to manipulate various PList data types, the original version of JPList made all PList datatypes implement a single interface, even though the underlying storage characteristics and data types were different. For example, a StringElement wraps a String, but an ArrayElement wraps a List of IElements, and a DictionaryElement wraps a Map of String keys and IElement values. Generics were applied in two steps to solve this problem.

First, the storage characteristics were unified across the data type implementations, while keeping the API consistent. Thus a StringElement now wraps a List of Strings, an ArrayElement wraps a List of IElements, and a DictionaryElement wraps a List of NameValuePairs.

Second, the IElement interface is now defined for a datatype <E>. This allows us to tighten the interface and assert that an implementation of IElement<String> will return a List<String> from its getContent() method instead of a List of untyped Objects, will take a String in its addContent() method instead of an untyped Object, and so on.

Code comparison links:

Top

Minimize changes to client code

The effect of unifying the storage characteristics across datatype implementations meant that we could now refactor out most of the getContent/setContent/addContent code into a common generic superclass. At this point we had two choices:

The second approach was chosen since this is closer to the original API and because it minimizes changes in the client code. It also ties in nicely with our next goal, minimizing the client's need to know about JPList internals.

Code comparison links:

Java 1.4 version Java 1.5 version
- Element.java
StringElement.java StringElement.java
DataElement.java DataElement.java
ArrayElement.java ArrayElement.java
DictionaryElement.java DictionaryElement.java

Top

Minimize client's need to know about JPList internals

Prior to the generification of IElement and its concrete typed implementations, a client would have to cast the return types to the appropriate underlying Java data type. This is no longer required. The data type returned from a concrete non-generic subclass is the value that is baked in at compile-time.

Top

Ensure Backward compatibility

Since most people are still looking for a compelling reason to switch their code to Java 1.5, there is a large user base who will not use a library that is built specifically for it. To serve that user base, we provide two separate builds of JPList, one for Java 1.4 and another for Java 1.5.

Clients can use either package by copying the jplist-xx-j4.jar or jplist-xx-j5.jar to their classpath. The xx stands for the release number for jplist, and the j4 and j5 stands for the Java 1.4 and Java 1.5 builds respectively. Moreover, a client can choose to operate in pure 1.4, pure 1.5 or mixed mode. These are explained in the table below:

Mode Jar File JPList compliance level Client code compliance level
java-1.4 jplist-xx-j4.jar Java 1.4 Java 1.4
java-1.5 jplist-xx-j5.jar Java 1.5 Java 1.5
mixed jplist-xx-j5.jar Java 1.5 Java 1.4 (compiled with Java 1.5 with source=1.4)

This can be demonstrated by running the ant targets with the compliance property set to java-1.4, java-1.5 or mixed, like so:

ant -Dcompliance=java-1.5 clean compile compile-test test
  

Top

Replace StringBuffer with StringBuilder

Replace occurences of java.lang.StringBuffer with its non-threadsafe drop in replacement java.lang.StringBuilder. This is because for the most part, we do not need thread-safety in these situations, and we should not have to pay the price for a feature we do not need.

Code comparison links:

Top

Replace Iterator for loop with for-in

Replaced Iterator loops with for-in loops. This provides more type safety since we do not cast at runtime. And besides it looks more python-ish.

Code comparison links:

Top

Add Annotations to throttle Xlint warnings

The code for JPList under Java 1.5 is compiled with Xlint set up to report unchecked casts and deprecation warnings. For the most part this is useful, but there are two places where we dont have control over the code where the warning is being generated. We have used the @SuppressWarnings annotation to throttle the warning in these cases. However, the annotation feature is currently a no-op in Java 1.5.0 (does not produce compile error, but does nothing), it is expected to be implemented in Java 1.5.1.

Top

Other changes made for Java 1.5

Apart from the changes described above, some more changes were made to the JPList codebase as a result of the move to Java 1.5, which are listed below:

Top

References