Saturday, 29 May 2010

Type-safe arbitary properties in Java

Yesterday a colleague approached me regarding the best method for associating arbitrary properties with an object in Java. The object currently has a Map<String,String> for which it exposes public setProperty and getProperty methods, but he wanted to be able to associate properties other than Strings, without having to serialize them.

I have seen Objects using a Map<String,Object> to store properties, but this leads to code becoming littered with casts, with the associated risks of ClassCastExceptions appearing at runtime.

My suggested solution was to use generics to hide the property map behind a type-safe facade.

First we define a generically typed Key:
public class Key<T> {

private final String name;

public Key(String name) {
this.name = name;
}

@Override
public int hashCode() {
return 37 * (name != null ? name.hashCode() : 0);
}

@Override
public boolean equals(Object obj) {
return this == obj;
}

@Override
public String toString() {
return "Key{" + name + "}";
}

}

When we use this as the key for our property map, the Java compiler can infer the Object type from the key, preventing us from associating a mistyped Object with a key, and correctly casting the Object back when we get it:
public class MyObject {

private Map<Key,Object> properties = new HashMap<Key, Object>();

public <T> void setProperty(Key key, T value) {
properties.put(key, value);
}

public <T> T getProperty(Key<T> key) {
return (T) properties.get(key);
}

}

The types can be determined at compile time, so there is no need for casts:
  MyObject obj = new MyObject();

Key kstr = new Key("foo");
Key kint = new Key("bar");

obj.setProperty(kstr, "string");
obj.setProperty(kint, 123);
String s = obj.getProperty(kstr);
Integer i = obj.getProperty(kint);

Errors will also be detected by the compiler:
  // Try to set a String value to an Integer-typed property
obj.setProperty(kint, "xyz");

// Try to cast the value of an Integer-typed property to a String
String x = (String) obj.getProperty(kint);

Tuesday, 27 April 2010

Creating PNGs from Jmol


Jmol is a fantastic open source 3D viewer for chemical structures. I've exported images from the application many times, both as PNGs and for rendering with POV-Ray, but after a bit of digging and trial & error I've figured out how to automate the process...

// Create the objects
java.awt.Canvas display = new Canvas();
org.jmol.adapter.smarter.SmarterJmolAdapter adapter = new SmarterJmolAdapter();
org.jmol.viewer.Viewer viewer = (Viewer)
Viewer.allocateViewer(display, adapter, null, null, null, null, null);
try {
viewer.setScreenDimension(new Dimension(1, 1));
viewer.scriptWait("load 'crystal.cif' {1 1 1};");
// can do more scripting here...
viewer.setScreenDimension(new Dimension(400, 400));

// anti-aliasing enabled
viewer.getGraphics3D().setWindowParameters(400, 400, true);

// Create image
viewer.getImageAs("PNG", 1, 400, 400, "image.png", null);
} finally {
// Ensure threads are stopped
viewer.setModeMouse(JmolConstants.MOUSE_NONE);
}

Using Jena3D's script for automatic orientation of structures, recently mentioned by Rolf Huehne on the Jmol-users mailing list I've been able to create some great pictures of crystal structures:

If only there was a really easy way for these to be used as place-holders for a lazy-loading Jmol applets, making websites embedding Jmol really fast... any volunteers?!