Note: The Clipboard API will be included in JDK1.1, however the Drag-and-Drop API will be in the following release (due to time constraints on 1.1). We are including the document on Drag-and-Drop here so that the Java Beans document can refer to it and we can get as much early feedback as possible.
java.awt.datatransfer.Transferable
A transferable object must be able to provide a list of formats (called " data flavors") for which it can provide the data, ordered from the most-richly descriptive flavor to the least. It must also be able to return the data (in the form of an Object reference) when requested in a particular flavor (or throw an exception if that flavor is not supported or the data is no longer available).
Convenience classes which implement the Transferable interface for common data types will be provided to make transfer of these common types easy for the developer. For example:
java.awt.datatransfer.StringSelection
The purpose of this API is to ensure that once the work goes into making a particular element or type of data transferable, it can be easily be passed around using any of the higher-level transfer protocols (clipboard, drag-and-drop, etc.).
This negotiation requires the definition of a data typing name-space such that these various flavors and data-types can be uniquely defined and recognized by distinct applications. In order to avoid the confusion of such overloaded terms such as "format", we have chosen the term "flavor" to represent this concept.
A data flavor is represented by an object which encapsulates all necessary information about a particular flavor to enable flavor negotiation and transfer between applications:
java.awt.datatransfer.DataFlavor
This information includes a logical name for the flavor (to enable programmatic identification), a human-presentable name (which be used to present to the user and would be localizable), and the representation class which is used to define the class of object used to actually transfer the data.
MIME type registration is currently handled by a third-party, the Internet Assigned Numbers Authority (IANA), enabling developers to easily locate the standard type/subtype name used for published data formats. Fortunately, formal registration is not required in order to define new MIME type/subtype names for less common formats (we agree that such a formal requirement would not be acceptable for basic Java data transfer). New type names can be created without formal registration by prepending "x-" in front of the name.
Object getTransferData(DataFlavor flavor)
is loosely defined to return an instance of class "Object" (for maximum flexibility), the DataFlavor's defined representation class becomes important to the receiving end of a transfer operation because it allows the returned object to be decoded unambiguously.
The current DataFlavor class defines two general kinds of data flavors:
MIME-type="application/x-java-serialized-object; class=<implemenation class>"
RepresentationClass=<implemenation class>
e.g. a DataFlavor representing an AWT GUI component:
MIME-type="application/x-java-serialized-object; class=java.awt.Component"
RepresentationClass=java.awt.Component
If the requesting side of a transfer operation asks for the data in this flavor,
it will be handed back an instance of the Component class.
MIME-type="application/<mime-subtype>"
RepresentationClass=java.io.InputStream
e.g. a DataFlavor representing RTF text:
MIME-type="application/rtf"
RepresentationClass=java.io.InputStream
If the requesting side of a transfer operation asks for the data in this fla-vor,
it will be handed back an InputStream instance from which it can read/
parse the RTF formatted text.
For a given MIME-type (type #2 above), a Java program is free to create multiple flavors with different representation classes. For example, in addition to providing the flavor for the MIME-type "application/rtf" above, a program could specify another flavor:
e.g. a DataFlavor representing RTF text:
MIME-type="application/rtf"
RepresentationClass=foobar.fooRTF
The DataFlavor concept may seem complex and confusing, however our intent is to make this as convenient as possible for developers by defining a set of commonly used data flavors.
package java.awt.datatransfer;
import java.io.*;
/**
* A class which implements the capability required to transfer a
* simple java String in plain text format.
*/
public class StringSelection implements Transferable, ClipboardOwner {
final static int STRING = 0;
final static int PLAIN_TEXT = 1;
DataFlavor flavors[] = {DataFlavor.stringFlavor, DataFlavor.plainTextFlavor};
private String data;
/**
* Creates a transferable object capable of transferring the
* specified string in plain text format.
*/
public StringSelection(String data) {
this.data = data;
}
/**
* Returns the array of flavors in which it can provide the data.
*/
public synchronized DataFlavor[] getTransferDataFlavors() {
return flavors;
}
/**
* Returns whether the requested flavor is supported by this object.
* @param flavor the requested flavor for the data
*/
public boolean isDataFlavorSupported(DataFlavor flavor) {
return (flavor.equals(flavors[STRING]) || flavor.equals(flavors[PLAIN_TEXT]));
}
/**
* If the data was requested in the "java.lang.String" flavor, return the
* String representing the selection, else throw an UnsupportedFlavorException.
* @param flavor the requested flavor for the data
*/
public synchronized Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
if (flavor.equals(flavors[STRING])) {
return (Object)data;
} else if (flavor.equals(flavors[PLAIN_TEXT])) {
return new StringReader(data);
} else {
throw new UnsupportedFlavorException(flavor);
}
}
public void lostOwnership(Clipboard clipboard, Transferable contents) {
}
}
The clipboard architecture relies on the data transfer mechanism defined by the Java Data Transfer API. The Clipboard API includes a single class which implements the data transfer model for a standard clipboard:
java.awt.datatransfer.Clipboard
and an interface which is implemented by any classes which will be writing data to the clipboard:
java.awt.datatransfer.ClipboardOwner
The Clipboard class provides two basic methods for reading-from/writing-to the clipboard:
void setContents(Transferable content, ClipboardOwner owner)
Transferable getContents(Object requestor)
The ClipboardOwner interface consists of a single method which is called if another object asserts ownership of the clipboard:
void lostOwnership(Clipboard clipboard)
To make the job of implementing clipboard operations on common data types easy for the developer, there will be convenience classes provided which implement the ClipboardOwner interface in a standard way:
java.awt.datatransfer.StringSelection
The following method in java.awt.Toolkit will provide access to the clipboard instance which interacts with the native platform facilities:
Clipboard getSystemClipboard();
The general sequence for implementing "paste":
(note: for simplicity this example uses a TextArea as the source/dest for the copy/paste operation; on most platforms, cut/copy/paste is already implemented for the TextArea and TextField classes within the native peers)
import java.awt.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
public class ClipboardTest extends Frame
implements ClipboardOwner, ActionListener {
TextArea srcText, dstText;
Button copyButton, pasteButton;
Clipboard clipboard = getToolkit().getSystemClipboard();
public ClipboardTest() {
super("Clipboard Test");
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setLayout(gridbag);
srcText = new TextArea(8, 32);
c.gridwidth = 2;
c.anchor = GridBagConstraints.CENTER;
gridbag.setConstraints(srcText, c);
add(srcText);
copyButton = new Button("Copy Above");
copyButton.setActionCommand("copy");
copyButton.addActionListener(this);
c.gridy = 1;
c.gridwidth = 1;
gridbag.setConstraints(copyButton, c);
add(copyButton);
pasteButton = new Button("Paste Below");
pasteButton.setActionCommand("paste");
pasteButton.addActionListener(this);
pasteButton.setEnabled(false);
c.gridx = 1;
gridbag.setConstraints(pasteButton, c);
add(pasteButton);
dstText = new TextArea(8, 32);
c.gridx = 0;
c.gridy = 2;
c.gridwidth = 2;
gridbag.setConstraints(dstText, c);
add(dstText);
pack();
}
public void actionPerformed(ActionEvent evt) {
String cmd = evt.getActionCommand();
if (cmd.equals("copy")) {
// Implement Copy operation
String srcData = srcText.getText();
if (srcData != null) {
StringSelection contents = new StringSelection(srcData);
clipboard.setContents(contents, this);
pasteButton.setEnabled(true);
}
} else if (cmd.equals("paste")) {
// Implement Paste operation
Transferable content = clipboard.getContents(this);
if (content != null) {
try {
String dstData = (String)content.getTransferData(
DataFlavor.stringFlavor);
dstText.append(dstData);
} catch (Exception e) {
System.out.println("Couldn't get contents in format: "+
DataFlavor.stringFlavor.getHumanPresentableName());
}
}
}
}
public void lostOwnership(Clipboard clipboard, Transferable contents) {
System.out.println("Clipboard contents replaced");
}
public static void main(String[] args) {
ClipboardTest test = new ClipboardTest();
test.show();
}
}