Archive

Monthly Archives: February 2009

Java doesn’t provide a mixin functionality in its language core :-( . But you can think of a mixin as an interface together with an implementation and then it is possible to use the concept of a mixin in java and use it as a means of promoting code reuse.

Like everyone during development I find myself writing classes that share a certain set of functionalities. In other words they share the same small number of methods. If you prefer object composition over inheritance (as you should :) ) you define an interface and make all the classes implement this interface. But then another thing jumps up. You end up writing the same method bodies in every class. So you create one implementation of this interface and let every class use this implementation and just delegate the method calls. And now you’ve effectively created a simple mixin using the delegator pattern and one interface implementation and ended up with a lot of builerplate code. Like this:

// simple interface
public interface Description {

    String getName();

    String getDescription();

    String getVersion();

    void setName(String name);

    void setDescription(String description);

    void setVersion(String version);
}

// one implementation
public final class DescriptionImpl implements Description {
    private String name;
    private String description;
    private String version;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    @Override
    public String toString() {
        return "" + name + " \"" + description + "\" (" + version + ")";
    }
}

// mixin usage
public class Bean implements Description {
    // mixing in the description to this bean
    private final Description description = new DescriptionImpl();

    // HERE IS THE BOILERPLATE DELEGATION CODE
    public void setVersion(String version) { description.setVersion(version); }

    public void setName(String name) { description.setName(name); }

    public void setDescription(String description) { this.description.setDescription(description); }

    public String getVersion() { return description.getVersion(); }

    public String getName() { return description.getName(); }

    public String getDescription() { return description.getDescription(); }

    @Override
    public String toString() { return description.toString(); }
}

I know that this is a really simplistic example but just try to imagine some complex operations taking place in the default interface implementation or possibly some generic code. Wouldn’t it be nice if with the help of an IDE you would be able to write this and end up with the same thing as in the above code fragment?

public class Bean implements Description {

    @Mixin private final Description description = new DescriptionImpl();

// and here imagine the collapsed auto-generated builderplate code here
}

The mixin can depend on the object that mixes it in:

public interface Upcase {

    String toUpcaseString();
}

public final class UpcaseImpl implements Upcase {
    private Object object;

    public UpcaseImpl(Object object) {
        this.object = object;
    }

    public String toUpcaseString() {
        return object.toString().toUpperCase();
    }
}

public class Bean implements Upcase {
    @Mixin private final Upcase upcase = new UpcaseImpl(this);

// and here imagine the collapsed auto-generated builderplate code here
}

The technology is present in (all) modern IDEs but I haven’t seen anything like this yet. Has anybody seen something similar in their IDE? Another question is: does anyone care? (Or is it just me :-) )

SpanTable = JTable + Cell span

While Swing provides a wide range of ready made components that cover a wide range of use cases most of the time if you find yourself needing something more you have to resort to writing it from scratch. After a couple of afternoons of searching the web for a decent open source solution I gave up and started looking for a tutorial that would help me understand the concepts underneath JTable and help me extend it to allow cell spanning. I found this tutorial which builds a fully functional JTable with cells that can span multiple columns. This was a nice start but I needed a little more.

Concepts

Adopting the division of cells from the above mentioned tutorial, there are 4 types of cells: hidden, visible, spanned and logical cells.

  • Visible cell – a cell that gets rendered
  • Hidden cells – a cell not rendered because it is spanned by another cell
  • Spanned cells – a cell which spans atleast 2 columns or rows (thereby hiding other cells)
  • Logical cells – every cell is a logical cell, a logical cell is used to query the model

A model holds information that the table displays, using an UI component, including the information about visible and hidden cells and cell spans. A custom UI object filters out the hidden cells to display only the visible ones.

Implementation

Span table class diagram

Span table and its dependencies

A SpanModel implementation

DefaultSpanModel is a basic implementation of the SpanModel interface. It uses java.util.Maps to store information about cell spans and hidden cells.

public class DefaultSpanModel extends ForwardingTableModel implements SpanModel {
    // decorated model
    private Map<Integer, Map<Integer, Integer>> rowSpans;
    private Map<Integer, Map<Integer, Integer>> columnSpans;
    private Map<Integer, Map<Integer, Cell>>    hiddenCells;

    /**
     * Constructs a new <code>DefaultSpanModel</code> backed by the supplied
     * <code>model</code>.
     *
     * @param model to be extended
     */
    public DefaultSpanModel(TableModel model) {
        super(model);

        Factory hashMapFactory = new Factory() {
            public Map<Integer, Integer> create() {
                return new HashMap<Integer, Integer>();
            }
        };

        rowSpans = MapUtils.lazyMap(new HashMap<Integer, Map<Integer, Integer>>(), hashMapFactory);
        columnSpans = MapUtils.lazyMap(new HashMap<Integer, Map<Integer, Integer>>(), hashMapFactory);
        hiddenCells = MapUtils.lazyMap(new HashMap<Integer, Map<Integer, Cell>>(), new Factory() {
            public Object create() {
                return new HashMap<Integer, Cell>();
            }
        });
    }

    // ...
}

Keep in mind that this is not the most efficient implementation as after the first rendering you’ll have (2 *(# of rows) + (# of columns)) of map instances but it does simplify the implementation of functionality and should keep rendering time independent of the number of spanned cells. Its a start :-) .

The most important trick to it is the custom UI object that filters hidden cells and only displays the visible ones. (Swing components delegate their rendering to utility UI objects to facilitate flexible look and feel as the above tutorial explains). This can be a bit of a problem to the generality of the solution. Because to render the span table you need a custom UI object but if someone changes the UI object then you don’t render the span table correctly. The good news is that most of the look and feels don’t change the rendering of tables (mostly only headers). As a precaution I set the UI object in the contructor and ignore any other UI component setting.

public class SpanTable extends JTable {
    private boolean isSpanModel;

    public SpanTable(TableModel model) {
        super(model);
        // the table UI has to be set to <code>SpanTableUI</code>
        this.setUI(new SpanTableUI());
    }

    @Override
    public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
        if (isSpanModel) {
            // if the model is a span model, we have to check if the cell is spanned or not
            // and expand the area of the cell to reflect it
            Rectangle cellRect = super.getCellRect(row, column, includeSpacing);

            for (int i = 1, n = ((SpanModel) getModel()).getRowSpan(row, column); i < n; i++) {
                // expand the area of the visible cell
                cellRect.height += getRowHeight(row + i);
            }

            for (int i = 1, n = ((SpanModel) getModel()).getColumnSpan(row, column); i < n; i++) {
                // expand the area of the visible cell
                cellRect.width += getColumnModel().getColumn(column + i).getWidth();
            }

            return cellRect;
        }

        return super.getCellRect(row, column, includeSpacing);
    }

    @Override
    public void setUI(TableUI ui) { }

    @Override
    public void setModel(TableModel dataModel) {
        isSpanModel = dataModel instanceof SpanModel;
        super.setModel(dataModel);
    }

    @Override
    public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
        // only needed for correct repaint behavior
        super.changeSelection(rowIndex, columnIndex, toggle, extend);
        repaint();
    }

    @Override
    public int columnAtPoint(Point point) {
        if(isSpanModel) {
           int row = super.rowAtPoint(point);
           int column = super.columnAtPoint(point);

           // return the column of the hiding cell
           return ((SpanModel) getModel()).getVisibleCell(row, column).getColumn();
        }

        return super.columnAtPoint(point);
    }

    @Override
    public int rowAtPoint(Point point) {
        if(isSpanModel) {
           int row = super.rowAtPoint(point);
           int column = super.columnAtPoint(point);

           // return the row of the hiding cell
           return ((SpanModel) getModel()).getVisibleCell(row, column).getRow();
        }

        return super.rowAtPoint(point);
    }
}

The most importat thing here is the expansion of the spanned cell rectangle in getCellRect().

Here is a simple usage example:

// DATA
String[][] data = new String[][] {
    {"a1", "a2", "a3", "a4"},
    {"b1", "b2", "b3", "b4"},
    {"c1", "c2", "c3", "c4"},
    {"d1", "d2", "d3", "d4"},
    {"e1", "e2", "e3", "e4"}
};

// SPAN TABLE INITIALIZATION
DefaultTableModel model = new DefaultTableModel(data, new String[] { "1", "2", "3", "4" });
DefaultSpanModel spanModel = new DefaultSpanModel(model);

aFrame.getContentPane().add(new JScrollPane(new SpanTable(spanModel)), BorderLayout.CENTER);

spanModel.setColumnSpan(0, 0, 3);
spanModel.setRowSpan(1, 1, 2);
spanModel.setColumnSpan(1, 1, 2);
spanModel.setRowSpan(2, 0, 3);

This is the final result:

A table with 3 spanned cells

A table with 3 spanned cells

A cell spanning 3 columns (edit)

A cell spanning 3 columns (edit)

A cell spanning 2 columns and 2 rows (edit)

A cell spanning 2 columns and 2 rows (edit)

A cell spanning 3 rows (edit)

A cell spanning 3 rows (edit)


Span table

Span table

Resources

source code

Follow

Get every new post delivered to your Inbox.