Unfurling Java's data transfer API

Learn to share data among applications with Java's data transfer classes

Return with me, if you will, to the early 1980s. Applications running on relatively inexpensive desktop hardware were just beginning to make a real difference in how work was done. The transformation began with VisiCalc in 1979. A few years later Ashton-Tate released dBASE II. Then, in 1982, Satellite Software released WordPerfect. By the time Lotus 1-2-3 appeared in 1983, the changes were nearly irreversible.

By 1984 users had numerous productivity packages to choose from. They could crunch numbers, write proposals, and query databases with near complete abandon. There was only one small problem. Most of the available applications sported only primitive data sharing capabilities (if they offered support for data sharing at all).

The consequence? In order to create a compound document composed of text, graphs, tables, and charts, a user had to cut and paste -- and I mean literally cut and paste.

Then along came Mac

In January 1984, Apple launched the Macintosh. Compared with its competitors, the Mac had several obvious advantages:

  • It had an easy-to-use desktop
  • Applications had a consistent graphical user interface
  • Applications and the system worked together and shared resources to a degree never seen before

One such shared resource was the clipboard. The clipboard allowed users to move data between applications by cutting or copying the data from one document (into the clipboard) and pasting it into another (from the clipboard).

The clipboard turned out to be a very powerful data transfer model. It worked so well, in fact, that to this day every GUI platform supports some form of clipboard functionality.

In order to be successful, Java can provide no less.

Java's requirements for data transfer

The architects of the Java class library had several requirements in mind when they designed the classes that implement data transfer.

First, the mechanism should be general enough to handle all current and any future data-transfer models. In particular, it should support both the clipboard model and the drag-and-drop model, using the same underlying machinery. Second, it should allow the programmer to create and transfer data types beyond those known or envisioned by the architects themselves. Finally, it should allow data transfer between Java applications and non-Java applications.

Let's take a look at what they came up with.

Data and data flavors

The data that applications transfer comes in many types -- text data and image data being two of the most obvious types. Each of these data types can appear in a number of different formats (or flavors, to use the term the Java architects selected).

Let's consider why a data type may exist in many flavors.

A application typically stores and transfers data in a data format designed to support the features the application provides. For example, a word processing application must store text as well as embedded formatting instructions, while a text editor stores only text. Each application's preferred storage format reflects this fact.

Now imagine what would happen if I tried to transfer data between the two applications. The word processor might understand plain text, but the text editor would surely be confused by the embedded formatting instructions. Considering that there are as many proprietary text formats as there are software vendors, the situation quickly degenerates into a hopeless mess.

In order to increase interoperability, applications should provide data for data transfer in as many flavors as possible. It is then up to the recipient to select a flavor it understands.

Here's a concrete example. Version 1.2 of a word processing application provides data in several flavors:

  • V1.2 flavor
  • V1.1 flavor
  • V1.0 flavor
  • Rich Text Format (RTF) flavor
  • Plain text

The V1.2 flavor is the preferred flavor for data transfer within the word processing application or between applications in the suite. Flavors V1.1 and V1.0 exist for backward compatibility. The RTF flavor exists for those applications that understand neither V1.2, V1.1, nor V1.0, but need formatting information. Plain text is the lowest common denominator.

Data flavors are represented by instances of the DataFlavor class. The DataFlavor class captures three important pieces of information about a data flavor:

  • A human-readable name
  • A logical name
  • The representation class

The human-readable name is a simple string naming the data flavor. It's recommended for human consumption only.

The logical name succinctly identifies a data flavor. Logical names are specified using the MIME notation standard (see RFC 1521). The MIME notation standard uses a type/subtype pair to identify a data type. The type field indicates the broad category, the subtype field indicates the particular brand within that category.

Some examples include:

  • text/html
  • text/plain
  • video/mpeg
  • audio/basic
  • image/jpeg
  • image/gif

The representation class identifies the class used to transfer the data. From a Java application's perspective, data is transferred as an instance of the representation class.

The Java 1.1 specification currently defines two basic types of data flavor.

The first is a data flavor representing a specific MIME type. The logical name is the MIME type itself, described above. The representation class is the InputStream class. The application can obtain the data being transferred by reading it from the InputStream instance.

The second is a data flavor representing a specific Java class. The logical name is:

application/x-java-serialized-object; class=[Java class]

The representation class is the Java class. An application can transfer any serializable Java class via this data flavor.

If neither of these fit the bill, you can subclass class DataFlavor to create custom data flavors.

The Transferable interface

Applications that want to transfer data do so via classes that implement the Transferable interface.

An instance of such a class represents the data being transferred in the data-transfer operation. The instance is created by the sender and passed between the applications.

The data transfer class definitions

Let's take a detailed look at the classes involved in data transfer.

Class java.awt.datatransfer.DataFlavor

The DataFlavor class defines methods useful for working with and comparing data flavors.

public String getHumanPresentableName()

The getHumanPresentableName method returns a human-readable string.

public String getMimeType()

The getMimeType method returns the logical name for this data flavor. This name is a string representation of the appropriate MIME type.

public Class getRepresentationClass()

The getRepresentationClass method returns the instance of the Class class that corresponds to the representation class. An object of this class will be used to transfer the data when requested in this data flavor.

public boolean isMimeTypeEqual(String strMimeType)
public boolean isMimeTypeEqual(DataFlavor dataflavor)

These methods compare this data flavor with either a MIME type or another data flavor to determine if they are equal.

Interface java.awt.datatransfer.Transferable

The Transferable interface defines the methods an object must implement in order to be transferred as part of a data transfer operation.

public DataFlavor [] getTransferDataFlavors()

The getTransferDataFlavors method specifies which data flavors the transfer data may be obtained as. The sending application must be ready and willing to provide data in every one of these flavors.

public boolean isDataFlavorSupported(DataFlavor dataflavor)

The isDataFlavorSupported method indicates whether or not the specified data flavor is supported by this transferable object.

public Object getTransferData(DataFlavor dataflavor)
    throws UnsupportedFlavorException,

The getTransferData method returns the representation class of the data.

A data transfer example

The transfer method shown next demonstrates how these two classes work together. The code is taken from a class that acts as the recipient of data transfers.

When a data transfer occurs, the application calls transfer. The method first checks to see if it can handle the transfer. If it can, it prints out information about the flavors provided and gets the data.

public void transfer(Transferable transferable) { if (transferable != null) { DataFlavor [] rgdataflavor = transferable.getTransferDataFlavors();

for (int i = 0; i < rgdataflavor.length; i++) { System.out.println(i + " = " + rgdataflavor[i].getHumanPresentableName() + " [" + rgdataflavor[i].getMimeType() + "]" );

if (rgdataflavor[i].isMimeTypeEqual(DataFlavor.stringFlavor)) { Object obj = null;

try { obj = transferable.getTransferData(rgdataflavor[i]); } catch (Exception ex) { ex.printStackTrace(); }

String str = (String)obj;

System.out.println("**"); System.out.println(str); System.out.println("**"); } } } }

The clipboard model

The data transfer mechanism described so far provides only the most basic functionality. The most obvious omission is a clearly defined user interface. As it turns out, that's okay. This primitive data transfer API was designed so that more complete data transfer models could be built on top of it. The Java class library provides two such higher level models -- the clipboard model and the drag-and-drop model. Let's take a look at the clipboard model.

The clipboard model

The central feature of the model is the clipboard, which is basically a slot that holds a transferable object representing a single piece of data. The clipboard is shared among several clients. The transferable object represents the data, it is not the actual data.

Clients can put an object in the slot or they can get it from the slot. Putting an object in the slot corresponds to copying to or cutting to the clipboard. Getting an object from the slot corresponds to pasting from the clipboard.

One client at a time is designated as the clipboard owner. This is the client that last put an object in the slot. When another client gets the object from the slot and requests the data, the clipboard owner must provide the data. When a client puts an object in the slot and becomes the clipboard owner, the previous clipboard owner is notified.

The clipboard class definitions

Let's take a look at the classes that make up the clipboard model.

Class java.awt.datatransfer.Clipboard

A clipboard is an instance of class Clipboard.

An instance of the Clipboard class synchronizes access to the contents of the slot (or clipboard).

Two methods control access to the contents of the clipboard.

public void setContents(Transferable transferableContents,
                        ClipboardOwner clipboardowner)

The setContents method sets the contents and registers a new clipboard owner. The previous clipboard owner is notified.

public Transferable getContents(Object objRequestor)

The getContents method returns the contents of the clipboard. The sole parameter contains a reference to the object requesting the contents of the clipboard.

Interface java.awt.datatransfer.ClipboardOwner

A clipboard owner is an object that can write data to the clipboard. Clipboard owners implement that ClipboardOwner interface.

The clipboard owner must provide the data when an object gets the contents of the clipboard. Because the clipboard owner cannot predict when the data represented by the transferable instance held by the clipboard will be needed, it should keep the data around. To provide for proper clean up, the clipboard owner must be notified when someone else writes content to the clipboard (and gains ownership).

public void lostOwnership(Clipboard clipboard,
                          Transferable transferableContents)

The lostOwnership method is called by the clipboard when a clipboard owner loses ownership of the clipboard. This typically occurs when another clipboard owner sets the contents of the clipboard, overwriting the previous contents.

A clipboard application

The image below illustrates a clipboard-based application in action.

The clipboard screenshot

When the user clicks on the Grab Contents button, the application reads the contents of the system clipboard. The system clipboard is a special instance of the Clipboard class shared by all applications (Java and non-Java alike). It interfaces with the native GUI clipboard mechanism. The application displays information about the contents of the clipboard in the text area.

Because of the security risks involved in allowing untrusted code access to the system clipboard, the Java security manager in most browsers won't allow this code to run as an applet.

Therefore, in order to take it for a spin, you'll need to download the code and run it locally. Here's how:

  1. Download the source (the Howto.java file and its compiled Howto.class companion) from Resources.

  2. Run the application. At the command prompt, type:

    java Howto

Note: These instructions assume you've installed the Java Developer's Kit. Also, the code works only on machines running Windows 95/NT.

1 2 Page 1
Page 1 of 2