start page | rating of books | rating of authors | reviews | copyrights

Book Home Java Enterprise in a Nutshell Search this book

Chapter 6. Data Transfer

Contents:

The Data Transfer Framework
Cut-and-Paste
Drag-and-Drop
A Data Source
A Data Sink

Data transfer refers to the ability of an application to transfer selected data in a variety of ways. For example, an application can use data transfer to support moving data between its own subparts. An application can also use data transfer to exchange data with other Java applications that are running in the same Java VM or in another Java VM or with native applications that are not running in a VM at all. There are two commonly used metaphors for data transfer: cut-and-paste and drag-and-drop. Java 1.1 included a basic data transfer architecture and supported cut-and-paste. The Java 2 platform extends the architecture in minor ways and adds support for drag-and-drop.

6.1. The Data Transfer Framework

Both the cut-and-paste and drag-and-drop metaphors rely on the same underlying data transfer architecture. This architecture was defined in Java 1.1 in the java.awt.datatransfer package. It consists of the DataFlavor class, which describes data types and data formats, and the Transferable interface, which defines methods that must be implemented if data is to be transferred.

6.1.1. The DataFlavor Class

A data transfer mechanism requires a precise and portable way to specify the type of data to be transferred. This is necessary so that both parties to the transfer--the data source and the data sink--can agree on exactly what is being transferred. Since the data source and the data sink may be entirely different applications, the mechanism for describing a data type must be general and flexible.

In Java, the type of data being transferred is described by a java.awt.datatransfer.DataFlavor object. The DataFlavor class describes data types using MIME types. MIME defines standard types like "text/html" and "image/jpeg". Because Java programs often transfer data within the same Java VM or between VMs, the DataFlavor class also supports describing data types with Java class names. For example, to transfer a java.awt.Point object from one Java VM to another, the data transfer mechanism can simply serialize the Point object and send the resulting stream of bytes to the other Java VM, where the Point object can be deserialized. When doing data transfer between Java VMs in this way, the transfer of objects becomes totally transparent.

The DataFlavor class defines constants for several commonly used data flavors, including DataFlavor.stringFlavor, DataFlavor.plainTextFlavor, and (in Java 1.2) DataFlavor.javaFileListFlavor. To transfer another types of data, you must create a custom DataFlavor by specifying the MIME type or Java class of the data and a human-readable name for the data type. For example:

DataFlavor jpegFlavor = new DataFlavor("image/jpeg", "JPEG Image Data");
DataFlavor pointFlavor = new DataFlavor(java.awt.Point.class, 
                                        "Java Point Object");

6.1.2. The Transferable Interface

DataFlavor objects describe data types, but they contain no data themselves. Data to be transferred using cut-and-paste or drag-and-drop must be encapsulated in an object that implements the Transferable interface.

Data transfer occurs in a heterogeneous environment. When you design the data transfer capabilities of your application, you cannot know the other applications with which the user may eventually want to exchange data. Thus, for maximum flexibility, an application that exports data--a data source--typically offers its data in multiple formats. An application that exports text might offer to transfer that data in the form of a Java String object or as a stream of Unicode characters, for example. If the receiving application is a Java program, it may choose to request the data as a Java String, while a non-Java application would choose the stream of characters instead.

The Transferable interface defines three methods. getTransferDataFlavors(), returns an array of DataFlavor objects that represent the data formats in which the data may be transferred, while isDataFlavorSupported() asks whether a particular data flavor is supported. The third method, getTransferData(), performs the actual transfer. This method takes an argument that specifies the desired data flavor and returns an Object that represents the data in the specified format. If the specified data flavor is not supported, getTransferData() throws an UnsupportedFlavorException.

The return value for getTransferData() needs a little more explanation. The type of this object depends on the DataFlavor that was requested. For any DataFlavor, the getRepresentationClass() method returns a Java Class object that represents the type of object that will be returned by getTransferData(). When a DataFlavor represents data that is transferred as a serialized Java object, the return value of getTransferData() is simply a Java object of whatever type was transferred (e.g., a String or java.awt.Point object). When a DataFlavor represents a MIME type, the data is actually transferred between applications as a stream of bytes. In this case, getTransferData() returns a java.io.InputStream object from which you can read and parse these bytes.

Because text is the most frequently transferred data type, the java.awt.datatransfer package defines a StringSelection class that implements the Transferable interface for strings. This Transferable class supports two data flavors, the pre-defined DataFlavor.stringFlavor and DataFlavor.plainTextFlavor constants. Unfortunately, however, there is a problem with StringSelection. When the string is requested in plain text format, the getTransferData() method returns a java.io.Reader object instead of a java.io.InputStream. Because StringSelection is widely used, applications receiving DataFlavor.plainTextFlavor data may want to use instanceof to determine whether the return value is an InputStream (a byte stream) or a Reader (a Unicode character stream). Despite the problems with StringSelection, there is a long-standing bug in Sun's Java 1.1 and Java 1.2 implementations for Windows platforms that makes it indispensable. On those platforms, StringSelection is the only Transferable class that can successfully transfer text between a Java application and a native application.

Applications that display strings in JTextField, JTextArea, and related components already support textual data transfer, as these components have cut-and-paste support built in. In other words, you typically don't have to implement textual data transfer yourself. When you do need to implement data transfer, it is probably because you are transferring some specialized type of data. Example 6-1 shows how we can implement the Transferable interface to transfer java.awt.Color objects between Java applications.

Example 6-1. TransferableColor.java

import java.awt.Color;
import java.awt.datatransfer.*;
import java.io.*;

/**
 * This class is used to transfer a Color object via cut-and-paste or
 * drag-and-drop.  It allows a color to be transferred as a Color object
 * or as a string. Due to a long-standing bug in Java 1.1 and Java 1.2, 
 * transferring a color as a string to native Windows applications will
 * not work.
 */
public class TransferableColor implements Transferable {
  // This DataFlavor object is used when we transfer Color objects directly
  protected static DataFlavor colorFlavor =
    new DataFlavor(Color.class, "Java Color Object");

  // These are the data flavors we support
  protected static DataFlavor[] supportedFlavors = {
    colorFlavor,                  // Transfer as a Color object
    DataFlavor.stringFlavor,      // Transfer as a String object
    DataFlavor.plainTextFlavor,   // Transfer as a stream of Unicode text
  };

  Color color;                    // The color we encapsulate and transfer

  /** Create a new TransferableColor that encapsulates the specified color */
  public TransferableColor(Color color) { this.color = color; }

  /** Return a list of DataFlavors we can support */
  public DataFlavor[] getTransferDataFlavors() { return supportedFlavors; }

  /** Check whether a specified DataFlavor is available */
  public boolean isDataFlavorSupported(DataFlavor flavor) {
    if (flavor.equals(colorFlavor) || 
        flavor.equals(DataFlavor.stringFlavor) ||
        flavor.equals(DataFlavor.plainTextFlavor)) return true;
    return false;
  }

  /** 
   * Transfer the data.  Given a specified DataFlavor, return an Object
   * appropriate for that flavor.  Throw UnsupportedFlavorException if we
   * don't support the requested flavor.
   */
  public Object getTransferData(DataFlavor flavor) 
       throws UnsupportedFlavorException, IOException
  {
    if (flavor.equals(colorFlavor)) return color;
    else if (flavor.equals(DataFlavor.stringFlavor)) return color.toString();
    else if (flavor.equals(DataFlavor.plainTextFlavor))
      return new ByteArrayInputStream(color.toString().getBytes("Unicode"));
    else throw new UnsupportedFlavorException(flavor);
  }
}


Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.