<更新記録>
2007年 11月 25日
作成

姉妹サイト検索 Web検索


ITextViewer IDocument

準備

JFaceのテキスト操作は、org.eclipse.jface.textと、そのサブパッケージで行います。 プログラムのコンパイルと実行には、text.jarとjfacetext.jarの2つのモジュールが必要です。 私のEclipse3.3環境では、org.eclipse.text_3.3.0.v20070606-0010.jarとorg.eclipse.jface.text_3.3.0.v20070606-0010.jar でした。

JFaceのテキスト操作の性格

JFaceは、SWTから機能をある程度限定してよく書かれるコードを簡略化することが多いのですが、 テキスト操作の場合にはその逆で、SWTの機能をいろいろと拡張したAPIがたくさんあって、 org.eclipse.jface.textをはじめ、8つのパッケージが用意されています。 しかし一度理解してしまえば便利なので、手の込んだテキスト操作をしたいならばJFaceを使うべきだと思います。

基本

JFaceのテキスト操作の機能はたくさんあるので、まずは基幹部分から覚えることが必要です。 JFaceの基幹部分は、IDocumentとITextViewerです。 簡単にいうと、IDocumentは、データを格納する役割、ITextViewerは、ウィジェットを表示する役割を担っています。 ITextViewerは、実際にはorg.eclipse.swt.custom.StyledTextを作成し、それに対するいろいろな操作をラップします。 このことは、ITextViewer#getTextWidgetがStyledTextを返すことからも推測できます。

最もシンプルなプログラム

まず手始めに、最もシンプルなプログラムを書いてみます。
JFaceText.java
public class JFaceText1 extends ApplicationWindow {
	public JFaceText1() {
		super(null);
	}
	
	protected Control createContents(Composite parent) {
		ITextViewer textViewer = new TextViewer(parent, SWT.V_SCROLL | SWT.MULTI);
		parent.setSize(200, 200);		
		return parent;
	}
	
	public static void main(String[] args) {
		Window w = new JFaceText1();
		w.setBlockOnOpen(true);
		w.open();
		Display.getCurrent().dispose();
	}
}
JFaceによるテキストウィジェットの核はIDocumentとITextViewerでしたが、 そのうちのITextViewerだけを使っています。 実行するときちんとテキストエリアが表示されるのですが、文字を記入しようとすると例外が発生します。 文字の記入等は、ITextViewerが窓口となり、 その後実際に記入された文字を記憶する等の処理はIDocumentが行うことになっているのですが、 そのIDocumentがセットされていないためです。

今度は、IDocumentをITextViewerにセットしてみます。 すると、きちんと文字入力ができるようになります。 しかもコピーやペーストといった、テキストの基本操作も行うことができます。 これらの基本操作は、StyledTextをそのまま実装しただけでは、 いろいろなイベント処理等をプログラマが実装しなければ達成できない機能でした。
public class JFaceText1 extends ApplicationWindow {
	public JFaceText1() {
		super(null);
	}
	
	protected Control createContents(Composite parent) {
		ITextViewer textViewer = new TextViewer(parent, SWT.V_SCROLL | SWT.MULTI);
		textViewer.setDocument(new Document("初期文字列"));
		parent.setSize(200, 200);		
		return parent;
	}
	
	public static void main(String[] args) {
		Window w = new JFaceText1();
		w.setBlockOnOpen(true);
		w.open();
		Display.getCurrent().dispose();
	}
}

ContentAssistant

では次に、Eclipseでよく見かける、文字を途中まで入力したら、 その候補をずらっとリスト表示してくれるというサンプルプログラムをつくってみます。

前者がeclipseでのアシスタント、後者が今回作成するアシスタント。 基本的な機能のみ使用するので、かなり見劣りします。
この機能は、org.eclipse.jface.text.contentassistパッケージが提供しています。 今回作成するサンプルプログラムでは、ITextViewerとIDocumentの他に、以下のクラスとインターフェイスを使用します。
  • ICompletionProposal
  • IContentAssistantとその実装のContentAssistant
  • IContentAssistProcessor
  • ICompletionProposalとその実装のCompletionProposal

作成するサンプルのクラス関係は、以下のようになっています。

+------------------------+
| IContentAssitProcessor |
+------------------------+
	^
	| implement
	|
+--------------------------------+
| AbstractContentAssistProcessor |
+--------------------------------+
	^
	| extend
	|
+-------------------------------+
| StaticalContentAssitProcessor |---+
+-------------------------------+   |
    | used                          |
    |                               |
    @ use                           |
+------------------+          +---------------------+
| ContentAssistant |----------| ICompletionProposal |
+------------------+          +---------------------+
   @ used    |
   |         +-----+
   | use           |
+------+         +-------------+
| Main |@--------| ITextViewer |
+------+         +-------------+
                      @
                      |
                      |
                 +-----------+
                 | IDocument |
                 +-----------+
このうち、作成しなければならない部分は、AbstractContentAssistProcessorとStaticalContentAssistProcessor、 それにMainの3つです。

IContentAssistantインターフェイスは、実際に画面へ文字列の候補を表示する機能と、 候補から選ばれた文字列を実際に文章中に挿入するという役割を持ちます。 ContentAssistantクラスは、その実装です。 このクラスは、IContentAssistProcessorに仕事を委任します。 委任する仕事の内容は、現在のポインタ位置に挿入できる候補の作成です。 なので、画面に表示される候補は、IContentAssistProcessorが作成しているということになります。

IContentAssistProcessorインターフェイスを実装したクラスは、 IContentAssitantから、挿入する文字列の候補の作成を依頼されます。 その以来は、IContentAssistProcessor#computeCompletionProposalsを通して行われます。 computeCompletionProposalsの戻り値は、ICompletionProposal[]です。 ICompletionProposalの実装であるCompletionProposalは、 IDocumentの内容を、どこから、どこまで、何に置き換えるかの情報を保持しています。 ContentAssistantは、この戻ってきた情報を使って仕事をこなすわけです。

AbstractContentAssistProcessorは、今回のサンプルプログラムで作成するクラスのひとつです。 このクラスは、IContentAssistProcessorを実装しますが、抽象クラスとしています。 IContentAssistProcessorで定義されているどのメソッドもnullを返し、実際に仕事はこなさないからです。 (Java文法的にはabstractでなくともよいが、使い道が無いので。) しかしここで「仕事をしないメソッド」を定義し、それを実装することで、 次からいらないメソッドまで実装する必要がなくなります。 getCurrentWordメソッドは、IContentAssistProcessorの実装ではないのですが、 たいがいのIContentAssistProcessor実装クラスでは必要になります。 このメソッドは、現在の入力文字をサーチします。例えば、Documentの内容が "I am a prog"であったとして、現在入力中の単語"prog"を検出するのに使用します。 このメソッドの内容は、StaticalContentAssistProcessorのソースも見たほうが、わかりやすいと思うので、 今はまだよくわからなくても問題ありません。

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;

public abstract class AbstractContentAssistProcessor implements IContentAssistProcessor {
	
	protected Exception error;
	
	public ICompletionProposal[] computeCompletionProposals(
			ITextViewer viewer, int documentOffset) {
		return null;
	}
	
	public IContextInformation[] computeContextInformation(
			ITextViewer viewer, int documentOffset) {
		return null;
	}
	
	public char[] getCompletionProposalAutoActivationCharacters() {
		return null;
	}
	
	public char[] getContextInformationAutoActivationCharacters() {
		return null;
	}
	
	public IContextInformationValidator getContextInformationValidator() {
		return null;
	}
	
	public String getErrorMessage() {
		return (error == null) ? null : error.getMessage();
	}
	
	protected String getCurrentWord(IDocument document, int documentOffset) {
		StringBuffer currentWord = new StringBuffer();
		char ch;
		
		try {
			for (int offset = documentOffset - 1 ;
				offset >=0 && !Character.isWhitespace(ch = document.getChar(offset)) ;
				offset--) {
				currentWord.insert(0, ch);
			}
			return currentWord.toString();
		} catch (BadLocationException e) {
			error = e;
			return null;
		}
	}
}

StaticalContentAssistProcessorは、AbstractContentAssistProcessorの継承(実際には実装)です。 このクラスのcomputeCompletionProposalsメソッドは、インスタンス作成時に渡した文字列配列をもとに、 現在入力中の単語に合致する文字列のリストを作り出します(実際にはICompletionProposal[]ですが、意味的には同義)。 その処理の中で、AbstractContentAssistProcessor#getCurrentWord()を使用しています。

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposal;

public class StaticalContentAssistProcessor extends AbstractContentAssistProcessor {
	private String[] candidates;
	
	public StaticalContentAssistProcessor(String[] candidates) {
		this.candidates = candidates;
	}
	
	public ICompletionProposal[] computeCompletionProposals(
			ITextViewer viewer, int documentOffset) {
		List<ICompletionProposal> results = new LinkedList<ICompletionProposal>();
		Iterator<String> itCandidates = Arrays.asList(candidates).iterator();
		String currentWord = getCurrentWord(viewer.getDocument(), documentOffset);
		int currentWordLen = currentWord.length();
		
		while (itCandidates.hasNext()) {
			String currentCandidate = itCandidates.next();
			if (currentCandidate.startsWith(currentWord)) {
				results.add(
					new CompletionProposal(
						currentCandidate, 
						documentOffset - currentWordLen, 
						currentWordLen, 
						currentCandidate.length()
					)
				);
			}
		}
		
		return (results.size() == 0) ? null :
			results.toArray(new ICompletionProposal[results.size()]);
	}
}

Mainでは、すべてのウィジェットの作成と、ContentAssistantをイベントに関連づける操作を行います。

Main
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;

public class Main extends ApplicationWindow {
	
	public Main() {
		super(null);
	}
	
	protected Control createContents(Composite parent) {
		
		ITextViewer textViewer = new TextViewer(parent, SWT.MULTI | SWT.V_SCROLL);
		textViewer.setDocument(new Document());
		
		final ContentAssistant assistant = new ContentAssistant();
		IContentAssistProcessor processor = new StaticalContentAssistProcessor(
			new String[]{
				"America", "Africa", 
				"Brazil", 
				"Canada", "China", 
				"Japan", 
				"Zeon"
			}
		);
		assistant.setContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE);
		assistant.install(textViewer);
		
		textViewer.getTextWidget().addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				if (Character.isLetter(e.character)) {
					assistant.showPossibleCompletions();
				}
			}
		});
		
		parent.setSize(300, 300);
		return parent;
	}
	
	public static void main(String[] args) {
		Window w = new Main();
		w.setBlockOnOpen(true);
		w.open();
		Display.getCurrent().dispose();
	}

}


Powered by VeryEasyCMS