Tuesday, December 27, 2005

Emulating static import

It may seem a trivial feature in JDK5, but static import is growing on me. It especially helps me read code to see the flow of method calls without package prefixes.

So at work I'm on a JDK1.4.2 project and miss static import. The solution is quite simple: I put same-named private static methods at the bottom of my class files to emulate the static import, thus:

import javax.swing.UIManager;

/**
 * A toy example: not production code.
 * <p/>
 * Throwing <code>RuntimeException</code> is in poor taste.
 */
public class TopLevel {
    static {
        // Set up font smoothing
        System.setProperty("swing.aatext", "true");

        try {
            setLookAndFeel("net.java.plaf.windows.WindowsLookAndFeel");

        } catch (final IllegalAccessException e) {
            throw new RuntimeException(e);

        } catch (final UnsupportedLookAndFeelException e) {
            throw new RuntimeException(e);

        } catch (final InstantiationException e) {
            throw new RuntimeException(e);

        } catch (final ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    // Real work goes here.

    // Simple forwarding to emulate static import:
    private static void setLookAndFeel(final String name)
            throws ClassNotFoundException,
            InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException {
        UIManager.setLookAndFeel(name);
    }
}

It's not rocket science, but it makes my day just a little bit nicer.

Monday, December 19, 2005

New math functions in JDK6

I've been watching the releases of JDK6 closely and have noticed that the math facilities continue to mature. Following in the footsteps of java.lang.StrictMath in JDK5, I see that JDK6 adds more useful static methods (for each method, there are similar methods for floats):

double copySign(double magnitude, double sign)
Returns the first floating-point argument with the sign of the second floating-point argument.
int getExponent(double d)
Returns the unbiased exponent used in the representation of a double.
double nextAfter(double start, double direction)
Returns the floating-point number adjacent to the first argument in the direction of the second argument.
public static double nextUp(double d)
Returns the floating-point value adjacent to d in the direction of positive infinity.
double scalb(double d, int scaleFactor)
Return d × 2scaleFactor rounded as if performed by a single correctly rounded floating-point multiply to a member of the double value set.

The only drawback is that several common operations are still missing. I wish the JDK would provide the comprehensiveness of C's libm.

Wednesday, December 14, 2005

Reinventing the enum in Java

I'm working on a Java project which uses JDK 1.4.2, so I do not have the useful enum keyword available to me. What to do? Reinvent the enum, of course. I try following the pattern set by JDK5, suitable for JDK 1.4.2. Merry Christmas:

/**
 * <code>AbstractEnum</code> models <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Enum.html">the
 * JDK5 <code>enum</code> feature</a>.
 * <p/>
 * Extending classes should follow this example: <pre>
 * public class Suit
 *         extends AbstractEnum {
 *     public static final Suit CLUBS = new Suit("CLUBS");
 *     public static final Suit DIAMONDS = new Suit("DIAMONDS");
 *     public static final Suit HEARTS = new Suit("HEARTS");
 *     public static final Suit SPADES = new Suit("SPADES");
 *
 *     public static Suit[] values() {
 *          return (Suit[]) values0(Suit.class, new Suit[count(Suit.class)]);
 *     }
 *
 *     public static Suit valueOf(final String name) {
 *         return (Suit) valueOf0(Suit.class, name);
 *     }
 *
 *     private Suit(final String name) {
 *         super(name);
 *     }
 * }</pre>
 *
 * @author <a href="mailto:binkley@alumni.rice.edu">B. K. Oxley (binkley)</a>
 * @version $Id$
 * @since Dec 14, 2005 6:32:23 PM
 */
public abstract class AbstractEnum
        implements Comparable {
    private static final Map ALL_VALUES_BY_NAME = new HashMap(4);
    private static final Map ALL_VALUES_BY_ORDINAL = new HashMap(4);
    private static final Map ALL_COUNTS = new HashMap(4);

    /**
     * The ordinal of this enumeration constant (its position in the enum
     * declaration, where the initial constant is assigned an ordinal of zero).
     * <p/>
     * Most programmers will have no use for this field.  It is designed for use
     * by sophisticated enum-based data structures.
     */
    private final int ordinal = createNextOrdinal(getClass());

    /**
     * The name of this enum constant, as declared in the enum declaration. Most
     * programmers should use the {@link #toString} method rather than accessing
     * this field.
     */
    private final String name;

    /**
     * Gets all enum values of type <var>clazz</var> into <var>array</var>.  Use
     * {@link #count(Class)} to size the array parameter.
     * <p/>
     * Concrete subclasses <em>should</em> provide <code>values()</code> which
     * follows the example:
     * <pre>
     * public static Suit[] values() {
     *     return (Suit[]) valuesOf(Suit.class, new Suit[count(Suit.class)]);
     * }</pre>
     *
     * @param clazz the enum type
     * @param array the array
     *
     * @return the enum values
     *
     * @throws ClassCastException if <var>clazz</var> is not an enum class
     */
    protected static AbstractEnum[] values0(final Class clazz,
            final AbstractEnum[] array) {
        checkClassIsEnum(clazz);

        int i = 0;

        for (final Iterator valueIt = getValuesByOrdinal(clazz).iterator();
                valueIt.hasNext(); ++i)
            array[i] = (AbstractEnum) valueIt.next();

        return array;
    }

    /**
     * Gets the count of enums of type <var>clazz</var>, zero (0) if none.
     *
     * @param clazz the enum type
     *
     * @return the enum count
     *
     * @throws ClassCastException if <var>clazz</var> is not an enum class
     */
    protected static int count(final Class clazz) {
        checkClassIsEnum(clazz);

        return ALL_COUNTS.containsKey(clazz)
                ? ((Integer) ALL_COUNTS.get(clazz)).intValue()
                : 0;
    }

    /**
     * Gets an enum with the given <var>name</var>.
     * <p/>
     * Concrete subclasses <em>should</em> provide <code>valuesOf(String)</code>
     * which follows the example:
     * <pre>
     * public static Suit valueOf(final String name) {
     *     return (Suit) valueOf(Suit.class, name);
     * }</pre>
     *
     * @param clazz the enum class
     * @param name the enum name
     *
     * @return the corresponding enum value
     *
     * @throws ClassCastException if <var>clazz</var> is not an enum class
     * @throws IllegalArgumentException if <var>name</var> is not an enum name
     */
    protected static AbstractEnum valueOf0(final Class clazz,
            final String name)
            throws IllegalArgumentException {
        checkClassIsEnum(clazz);

        final Map values = getValuesByName(clazz);

        if (values.containsKey(name))
            return (AbstractEnum) values.get(name);

        throw new IllegalArgumentException(name);
    }

    /**
     * Constructs a new <code>AbstractEnum</code> with the given
     * <var>name</var>.
     *
     * @param name the name of this enum constant, which is the identifier used
     * to declare it
     */
    protected AbstractEnum(final String name) {
        this.name = name;

        getValuesByName(getClass()).put(name, this);
        getValuesByOrdinal(getClass()).add(this);
    }

    /**
     * Returns the name of this enum constant, exactly as declared in its enum
     * declaration.
     * <p/>
     * <b>Most programmers should use the {@link #toString} method in preference
     * to this one, as the toString method may return a more user-friendly
     * name.</b>  This method is designed primarily for use in specialized
     * situations where correctness depends on getting the exact name, which
     * will not vary from release to release.
     *
     * @return the name of this enum constant
     */
    public String name() {
        return name;
    }

    /**
     * Returns the ordinal of this enumeration constant (its position in its
     * enum declaration, where the initial constant is assigned an ordinal of
     * zero).
     * <p/>
     * Most programmers will have no use for this method.  It is designed for
     * use by sophisticated enum-based data structures.
     *
     * @return the ordinal of this enumeration constant
     */
    public int ordinal() {
        return ordinal;
    }

    // Object

    /**
     * Throws CloneNotSupportedException.  This guarantees that enums are never
     * cloned, which is necessary to preserve their "singleton" status.
     *
     * @return (never returns)
     *
     * @throws CloneNotSupportedException if called
     */
    protected Object clone()
            throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    /**
     * Returns the name of this enum constant, as contained in the declaration.
     * This method may be overridden, though it typically isn't necessary or
     * desirable.  An enum type should override this method when a more
     * "programmer-friendly" string form exists.
     *
     * @return the name of this enum constant
     */
    public String toString() {
        return name;
    }

    // Comparable

    /**
     * Compares this enum with the specified object for order.  Returns a
     * negative integer, zero, or a positive integer as this object is less
     * than, equal to, or greater than the specified object.
     * <p/>
     * Enum constants are only comparable to other enum constants of the same
     * enum type.  The natural order implemented by this method is the order in
     * which the constants are declared.
     */
    public int compareTo(final Object o) {
        if (!getClass().equals(o.getClass()))
            throw new ClassCastException(o.getClass().toString());
        return ordinal - ((AbstractEnum) o).ordinal;
    }

    private static Map getValuesByName(final Class clazz) {
        final Map values;

        if (ALL_VALUES_BY_NAME.containsKey(clazz))
            values = (Map) ALL_VALUES_BY_NAME.get(clazz);
        else
            ALL_VALUES_BY_NAME.put(clazz, values = new HashMap(8));

        return values;
    }

    private static SortedSet getValuesByOrdinal(final Class clazz) {
        final SortedSet values;

        if (ALL_VALUES_BY_ORDINAL.containsKey(clazz))
            values = (SortedSet) ALL_VALUES_BY_ORDINAL.get(clazz);
        else
            ALL_VALUES_BY_ORDINAL.put(clazz, values = new TreeSet());

        return values;
    }

    private static int createNextOrdinal(final Class clazz) {
        final int count = count(clazz);

        ALL_COUNTS.put(clazz, new Integer(count + 1));

        return count;
    }

    private static void checkClassIsEnum(final Class clazz) {
        if (!AbstractEnum.class.isAssignableFrom(clazz))
            throw new ClassCastException(clazz.toString());
    }
}