Union and intersection types in Java

Ceylon has union and intersection types at the heart of its type system. Ironically, there is something in Java resembling these powerful concepts, but only in specialized niche contexts and not as “first class citizens” of the type system:

  1. Something like intersection types can be used in generic type boundary declarations (since Java 1.5)
  2. Something like union types can be declared in try-catch blocks that use the multi-catch feature (since Java 1.7)

Interestingly, the Java language engineers used the & and | operators, just like Ceylon. But I have found no evidence so far that they are considering expanding these concepts to the Java type system in general or even let Java developers use them freely in all type declarations …

Update Oct/2012: At JavaOne, I asked Brian Goetz about this. He said that the Java Language team at Oracle has no intention to add additional support – beyond the niches mentioned above – for Union or Intersection types to Java in the foreseeable future.

Autocomplete combobox using JQuery UI

Disclaimer, Sept 2013: The following worked for me with an older JQuery version, but not with 1.9 or later. The code is provided as is and might be useful for some people, but due to time constraints I cannot assist with problems you might run into.

The example at http://jqueryui.com/demos/autocomplete/combobox.html illustrates how you can use JQuery UI to turn any HTML <select> element into an auto-complete combobox.

Auto-complete means in this context that the select options are narrowed down by what the user has typed in so far.

The example has a few bugs and shortcomings, some of which have been fixed here. I build on this work and further fixed it up to support dynamic updates of the options list through event-triggered AJAX calls. You can find the source code of the resulting JQuery UI auto-complete combobox widget at the end of this post.

Expose it to your webapp as includes/js/combobox.js and include it from your HTML or JSP page like this (adjust the path according to your JQuery UI installation):

<script type="text/javascript" src="includes/js/jquery-ui-1.8.4/js/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="includes/js/jquery-ui-1.8.4/js/jquery-ui-1.8.4.custom.min.js"></script>
<script type="text/javascript" src="includes/js/combobox.js"></script>

And let’s say your <select> component looks like this:

<select id="mySelect">
  <option value="1">one</option>
  <option value="2">two</option>
  <option value="3">three</option>
  <!-- etc. -->
</select>

Then make it an auto-complete combobox widget like this:

$(document).ready(function() {
   $("#mySelect").combobox();
}

You can update the options dynamically using JQuery’s load() function, typically triggered by an event. The interesting part is that I implemented the internal _create() and _init() functions of the widget so that reinitialization through additional calls to combobox() are supported:

$("#mySelect").load(
    "dataRequest.do", // this has to be your own AJAX handler that generates the new options as HTML
    function() { // this callback function is invoked after the AJAX call has returned
        // then refresh the combobox widget (to use the updated options)
        $("#mySelect").combobox();
    }
);
/*
 Based on code from
 http://www.melrosesolutions.com/blog/index.cfm/2010/7/16/Turn-SELECTs-into-Comboboxes-with-jQuery-UI-Autocomplete
 */

(function($) {

    $.widget("ui.combobox", {

        _create: function() {
            var select = this.element;

            if (select.is(":disabled") 
                    || select.hasClass("inputOverlayCreated")) {
                return;
            }

            select.hide();

            // set up input text element
            var input = $("<input type='text'>");
            input.insertAfter(select);

            // remember that combobox is creates (to avoid odd duplicates)
            select.addClass("inputOverlayCreated");

            // the class ui-combobox-content is required for proper
            // highlighting of data changes
            input.addClass("ui-combobox-content ui-widget ui-corner-left");

            //clear text when user clicks in text input
            input.click(function() {
                $(this).val("");
            });

            input.attr("menustatus", "closed");

            // over-ride form submit, so it can't submit
            // if the menu is open
            var form = $(input).parents('form:first');
            $(form).submit(function(e) {
                return (input.attr('menustatus') == 'closed');
            });

            // set up button for fake 'select'
            var btn = $("<button>&nbsp;</button>");
            btn.attr("tabIndex", -1);
            btn.attr("title", "Show All Items");
            btn.insertAfter(input);
            btn.button({
                icons: {
                    primary: "ui-icon-triangle-1-s"
                },
                text: false
            });
            btn.removeClass("ui-corner-all");
            btn.addClass("ui-corner-right ui-button-icon");
            btn.click(function() {
                //event.preventDefault();
                // close if already visible
                if (input.autocomplete("widget").is(":visible")) {
                    input.autocomplete("close");
                    return false; // return false, so form isn't automatically submitted
                }
                // pass empty string as value to search for, displaying all results
                input.autocomplete("search", "");
                input.focus();
                return false; // return false, so form isn't automatically submitted
            });

            // add some styles
            btn.css("margin-left", "-1px");
            btn.css("padding", 0);
            $('span.ui-button-text', btn).css("padding", 0);

            input.css("margin", 0);
            input.css("padding", "0 0.4em 0 0.4em");
            input.css("width", select.outerWidth() - btn.outerWidth(true) - 10);// match the width
        },

        _init : function() {
            var select = this.element;

            if (select.is(":disabled")) {
                // we don't apply any fancy combobox behaviour at all
                // if the underlying drop-down list is disabled
                return;
            }

            var opts = new Array();
            $('option', select).each(function(index) {
                var opt = new Object();
                opt.val = $(this).val();
                opt.label = $(this).text();
                opts[opts.length] = opt;
            });

            var input = select.next();

            // initialise text with what's currently selected
            input.val($(':selected', select).text());

            input.autocomplete({
                source: opts,
                delay: 0,
                change: function(event, ui) {
                    if (!ui.item) {
                        // user didn't select an option, but what they typed may still match
                        var enteredString = $(this).val();
                        var stringMatch = false;
                        for (var i = 0; i < opts.length; i++) {
                            if (opts[i].label.toLowerCase() == enteredString.toLowerCase()) {
                                select.val(opts[i].val);// update (hidden) select
                                $(this).val(opts[i].label);// corrects any incorrect case
                                select.trigger("change");
                                stringMatch = true;
                                break;
                            }
                        }
                        if (!stringMatch) {
                            // remove invalid value, as it didn't match anything
                            $(this).val($(':selected', select).text());
                        }
                        return true;
                    }
                },
                select: function(event, ui) {
                    select.val(ui.item.val);// update (hidden) select
                    select.trigger("change");
                    return true;
                },
                // stop parent form from being while menu is open
                open: function(event, ui) {
                    input.attr("menustatus", "open");
                },
                close: function(event, ui) {
                    input.attr("menustatus", "closed");
                },
                minLength: 0
            });
        }
    });

})(jQuery);

Doing HTML5

It’s refreshing to take a Dive Into HTML5.

Reading the HTML5 spec is even more fun. It’s only 600+ pages after all … :)

My personal “IT résumé” website at odoepner.github.io has recently become HTML5 compliant (using W3C validator). It was mostly a matter of changing the doctype and cleaning up the existing XHTML somewhat.

While I was at it, I also added in some CSS3 to make it look a little nicer. The result is tested on most browsers.

Generic Apache Ant build.xml for webapps with JUnit tests

This post is about a build.xml file for Apache Ant that compiles, unit tests and packages a typical web application. The main build artifact is a timestamped WAR file.

Standardized directory layout

The build file assumes that your project uses the following directories:

  • src : Java sources and classpath resources
  • test : JUnit test sources and classpath resources
  • web : Web resources (JSP, HTML, CSS, etc.)
  • web/WEB-INF/lib : jars required by the web application at runtime
  • devlib/provided : jars provided by the web container at runtime
  • devlib/test : jars required for test compilation or execution

Transient build directories

The build will create these transient output directories for each stage of the build process. You should configure the version control of your project to ignore them.

  • build/classes : Compiled Java code and classpath resources (compile)
  • build/test-classes : Compiled JUnit tests (test-compile)
  • build/test-reports : JUnit test reports (test)
  • dist : WAR file (dist)

Run ant clean to delete them after you grabbed the WAR file.

The build.xml file

<project name="PROJECT_NAME" default="dist">

    <property name="classes" location="build/classes"/>
    <property name="test-classes" location="build/test-classes"/>
    <property name="test-reports" location="build/test-reports"/>

    <path id="classpath">
        <fileset dir="devlib/provided">
            <include name="*.jar"/>
        </fileset>
        <fileset dir="web/WEB-INF/lib">
            <include name="*.jar"/>
        </fileset>
    </path>

    <path id="test-classpath">
        <path refid="classpath"/>
        <pathelement location="${classes}"/>
        <pathelement location="${test-classes}"/>
        <fileset dir="devlib/test">
            <include name="*.jar"/>
        </fileset>
    </path>

    <target name="clean">
        <delete dir="dist"/>
        <delete dir="build"/>
    </target>

    <target name="compile">
        <mkdir dir="${classes}"/>
        <javac destdir="${classes}">
            <classpath refid="classpath"/>
            <src path="src"/>
            <src path="common/src"/>
        </javac>
        <copy todir="${classes}">
            <fileset dir="src" excludes="**/*.java"/>
        </copy>
    </target>

    <target name="test-compile" depends="compile">
        <mkdir dir="${test-classes}"/>
        <javac destdir="${test-classes}">
            <src path="common/test"/>
            <src path="test"/>
            <classpath refid="test-classpath"/>
        </javac>
    </target>

    <target name="test" depends="test-compile">
        <mkdir dir="${test-reports}"/>
        <junit printsummary="yes">
            <classpath refid="test-classpath"/>
            <formatter type="brief" usefile="false"/>
            <formatter type="xml"/>
            <batchtest todir="${test-reports}" failureproperty="failed"
                       errorproperty="failed">
                <fileset dir="${test-classes}" includes="**/*Test.class"/>
            </batchtest>
        </junit>
    </target>

    <tstamp>
        <format property="timestamp" pattern="yyyy-MM-dd_hh-mm"/>
    </tstamp>

    <target name="dist" depends="clean, compile, test">
        <mkdir dir="dist"/>
        <war destfile="dist/${ant.project.name}_${timestamp}.war"
             basedir="web"
             excludes="**/CVS">
            <classes dir="${classes}"/>
        </war>
    </target>

</project>

Common entries in .bash_aliases

I keep my bash aliases grouped in separate files. I include them from .bashrc like this:

# Alias definitions that should work everywhere
if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

# Alias definitions that are specific to your distro
# e.g. Cygwin-only stuff, apt-get shortcuts on Debian, etc.
if [ -f ~/.bash_aliases.distro-specific ]; then
    . ~/.bash_aliases.distro-specific
fi

This is the ~/.bash_aliases that I commonly use:

# Interactive verbose operation...
alias rm='rm -iv'
alias cp='cp -iv'
alias mv='mv -iv'

# Default to human readable figures
alias df='df -h'
alias du='du -h'

# Misc :)
alias less='less -r'                          # raw control characters
alias whence='type -a'                        # where, of a sort
alias grep='grep --color'                     # show differences in colour
alias egrep='egrep --color=auto'              # show differences in colour
alias fgrep='fgrep --color=auto'              # show differences in colour

# Some shortcuts for different directory listings
alias ls='ls -hF --color=tty'                 # classify files in colour
alias dir='ls --color=auto --format=vertical'
alias vdir='ls --color=auto --format=long'
alias ll='ls -l'                              # long list
alias la='ls -A'                              # all but . and ..
alias l='ls -CF'                              #

This is the ~/.bash_aliases.distro-specific that I use on Debian (or other APT-based distros):

alias show='apt-cache show'
alias search='apt-cache search'

alias files='dpkg -L'
alias selections='dpkg --get-selections'

alias install='sudo apt-get install'
alias reinstall='sudo apt-get install --reinstall'

alias update='sudo apt-get update'
alias upgrade='sudo apt-get upgrade'

alias remove='sudo apt-get remove'
alias purge='sudo apt-get remove --purge'
alias autoremove='sudo apt-get autoremove'

Iterate in reverse order over a list in Java

Sometimes we want to iterate in reverse order over a list in Java – without (re)sorting the list. Code example:

    for (T item : backwards(list)) {
        // do something
    }

Here is a backwards() method that does this for you (maybe put this code into a util class and use a static import):

    public static <T> Iterable<T> backwards(final List<T> list) {
        return new Iterable<T>() {
            @Override
            public Iterator<T> iterator() {
                return backwardsIterator(list);
            }
        };
    }

    private static <T> Iterator<T> backwardsIterator(List<T> list) {
        final ListIterator<T> iter = list.listIterator(list.size());
        return new Iterator<T>() {
            @Override
            public boolean hasNext() {
                return iter.hasPrevious();
            }

            @Override
            public T next() {
                return iter.previous();
            }

            @Override
            public void remove() {
                iter.remove();
            }
        };
    }