最終更新日:1997 年 8 月 13 日
目的
典型的な GUI ユーザは、カット、コピー、ペースト、およびドラッグ&ドロップのような操作を使用して、アプリケーション間でデータが転送できることを期待しています。現在の Java 環境で、このための唯一の機構は、AWT ネイティブウィジェット (TextField、TextArea) がデフォルトで提供している機能を介するものです。しかし、これらのネイティブウィジェットが対応していない所で、そのような操作を有効にする必要のあるケースが多くあります。そのため、Java プラットフォームは、基本的なデータ転送機能を使用可能にするために、API を提供する必要があります。このドキュメントでは、様々な転送プロトコルをその上に構築できる Java オブジェクトに対するデータ転送機能の基準線となるものを定義します。このドキュメントではまた、これら上位レベル転送プロトコルの 2 つの API、クリップボードおよびドラッグ&ドロップについて説明します。
注: クリップボード API は JDK 1.1 に含まれていますが、ドラッグ&ドロップ API は (1.1 の時間的制約により) 次のバージョンに含まれる予定です 。ここでは、JavaBeans ドキュメントから参照でき、できるだけ早くフィードバックが得られるように、ドラッグ&ドロップに関するドキュメントが含まれています。
java.awt.datatransfer.Transferable
Transferable オブジェクトは、書式 (データフレーバと呼ばれる) の並びを提供し、この並びに対して、もっとも詳細に記述するフレーバからもっとも簡素なものまで、順序付けられたデータを提供できなければなりません。また、特定のフレーバで要求されたとき、(オブジェクト参照の形式で) データを返すことができなければなりません。あるいは、そのフレーバがサポートされていない場合、またはそのデータがもはや使用できない場合には、例外をスローします。
よく用いられるデータ型に Transferable インタフェースを実装した簡易クラスが、開発者がこれらの共通型の転送を容易にできるように提供されています。例を示します。
java.awt.datatransfer.StringSelection
この API の目的は、特定の要素またはデータ型を転送可能にする作業に入ったとき、上位レベル転送プロトコル (クリップボード、ドラッグ&ドロップなど) のどれかを使用して、簡単にそれを渡せることを保証することです。
データフレーバ
共通データ転送操作 (クリップボート、ドラッグ&ドロップ) の典型的様相とは、データを転送するフレーバをめぐっての、提供者と要求者の間の交渉です。たとえば、html テキストがブラウザで選択され、ほかのワードプロセッサアプリケーションにコピーされ、ペーストされるとき、ペースト操作の可能な対象アプリケーションの数を最大にするために、ブラウザは通常データを複数のフレーバ (通常、html フォーマットのテキストおよびプレーンな ASCII) で提供します。
この交渉には、個別のアプリケーションでこれら様々なフレーバとデータ型が一意に定義され認識できる、データ型分類用名前空間の定義が必要です。「フォーマット」などの使用頻度の高い用語との混同を避けるために、「フレーバ」という用語を使ってこの概念を表します。
データフレーバは、アプリケーション間のフレーバ交渉と転送を可能にするために、特定のフレーバに関する必要な全情報をカプセル化したオブジェクトによって表します。
java.awt.datatransfer.DataFlavor
この情報には、フレーバの論理名 (プログラムからの識別を可能にする)、ユーザに表示する名前 (ユーザに示すために使用し、各言語対応が可能なものもある)、データを実際に転送するオブジェクトクラスを定義するために使用する表現クラスなどが含まれます。
MIME 形式の登録は、現在 Internet Assigned Numbers Authority (IANA) というサードパーティが行なっており、公開データフォーマット用に使用する標準形式/サブ形式名を簡単に検索できるようになっています。あまり一般的でないフォーマットに対して新しい MIME 形式/サブ形式名を定義するためには、公式の登録は必要ありません (このような公式の要件が、基本的な Java データ転送では必要ないのは好ましい)。新しい形式名は公式な登録はせずに、名前の前に "x-" を追加して作成できます。
Object getTransferData(DataFlavor flavor)
は、(柔軟性を最大にするために) 「Object」クラスのインスタンスを返すように緩やかに定義されているために、DataFlavor が定義した表現クラスは転送操作の終端では重要になります。これにより、返されたオブジェクトがあいまいさなしにデコードされるようになるからです。
現在の DataFlavor クラスは、次の 2 つの一般的なデータフレーバを定義します。
MIME-type="application/x-java-serialized-object; class=<implemenation class>"
RepresentationClass=<implemenation class>
たとえば、AWT GUI コンポーネントを表す DataFlavor を次に示します。
MIME-type="application/x-java-serialized-object; class=java.awt.Component"
RepresentationClass=java.awt.Component
転送操作の要求側がこのフレーバのデータを求める場合、
Component クラスのインスタンスが戻されます。
MIME-type="application/<mime-subtype>"
RepresentationClass=java.io.InputStream
たとえば、RTF テキストを表す DataFlavor を次に示します。
MIME-type="application/rtf"
RepresentationClass=java.io.InputStream
転送操作の要求側がこのフレーバのデータを求める場合、
RTF 形式のテキストを読み込み
および解析できる InputStream のインスタンスが戻されます。
与えられた MIME 形式 (上の 2 つ目のタイプ) については、Java プログラムでは異なる表現クラスを持つ複数のフレーバを自由に作成できます。たとえば、上記の MIME 形式 application/rtf にフレーバを提供することに加えて、プログラムは別のフレーバを指定することもできます。
たとえば、RTF テキストを表す DataFlavor を次に示します。
MIME-type="application/rtf"
RepresentationClass=foobar.fooRTF
DataFlavor の概念は、複雑で混乱を引き起こすように思えます。しかし、意図しているのは、共通に使用するデータフレーバのセットを定義して、これを開発者ができるだけ使用しやすくすることです。
複数項目の転送
転送プロトコルが、単一の転送操作で、複数の別個のデータの転送をサポートすること (すなわち、ファイルマネージャアプリケーションから、複数のファイルのアイコンをドラッグ&ドロップすること) は、一般的ではありません。転送 API は、複数のデータ項目の同時転送をある形式でサポートする必要があります。現在の提案は、個別データオブジェクトの集まりを扱うことができる Transferable を実装して、この機能をカプセル化することです。この計画は現在綿密に調査されており、この提案を将来改訂するときに詳細に検討されます。
Transferable オブジェクト作成のコード例
次のコードは StringSelection クラスソースを示します。これはプレーンな Unicode テキストを転送できるクラスを作成するための API の使用方法の例です。
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) {
}
}
クリップボードアーキテクチャは、Java データ転送 API が定義するデータ転送機構に依存しています。クリップボード API には、標準クリップボード用のデータ転送モデルを実装する次の単一のクラスがあります。
java.awt.datatransfer.Clipboard
データをクリップボードに書き込んでいるクラスが実装する次のインタフェースがあります。
java.awt.datatransfer.ClipboardOwner
Clipboard クラスは、クリップボードとの読み込みおよび書き込みのための次の 2 つの基本的メソッドを提供します。
void setContents(Transferable content, ClipboardOwner owner)
Transferable getContents(Object requestor)
ClipboardOwner インタフェースは次の単一のメソッドから成り、別のオブジェクトがクリップボードの所有権を主張した場合に呼び出されます。
void lostOwnership(Clipboard clipboard)
開発者が一般的なデータ型にクリップボード操作を実装する作業を簡単に行えるように、標準的な方法で ClipboardOwner インタフェースを実装する簡易クラスが提供されます。
java.awt.datatransfer.StringSelection
java.awt.Toolkit 内の次のメソッドは、ネイティブプラットフォーム機能とのインタフェースを持つクリップボードのインスタンスへのアクセスを提供します。
Clipboard getSystemClipboard();
「ペースト」を実装するプログラムの一般的操作手順を次に示します。
注:簡単にするために、コピーとペーストの操作元および操作先として TextArea を使用します。ほとんどのプラットフォームで、カット、コピー、ペーストはすでにネイティブピア内の TextArea および TextField 用に実装されています。
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();
}
}