読者です 読者をやめる 読者になる 読者になる

テキストカラーリング用のEditorKitを自作する

自分でXMLをカラーリングしてくれるエディタを作ろうとしてとりあえず
StyledDocumentからgetTextしたのを解析してタグ部分にsetCharacterAttributesしてみたもののオーバーヘッドがすごいことに。
(一文字入力するごとに全体を再構成しなおすという適当な実装にも問題がある気もするけど)
ファイルが1000行超えてると40msくらい。キー押しっぱなしで連続入力するとちょっとカクつく程度なんだけどやはり気になる。
ファイルの解析部分は1msもかかってないようなのでsetCharacterAttributesするごとに一々ビューが更新されてるっぽい?


モデルをいじるのは駄目だな、ということでビュー部分をいじるってみるということに。
JEditorPaneに登録する専用のEditorKitを作るのが正攻法っぽいかな、ということで作ろうとしたものの解説してくれているところが中々無い。
おそらくJavaでクライアントサイドのなんてあんまり組まれないのだろう・・・。
それでもなんとか見つけた役立ちそうなさいとはここ
なんとかがんばって複雑で古くそのままじゃコンパイルも通らないサンプルコードを解読し始めてみる。
↑2週間くらい前の話。

でやっと単純にタグ部分を青色にできるようになったのがこちら。
XMLEditorKit.java

import javax.swing.text.DefaultEditorKit;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
import javax.swing.text.ViewFactory;

public class XMLEditorKit extends DefaultEditorKit {

    public XMLEditorKit() {
	super();
    }

    public XMLContext getStylePreferences() {
	if (preferences == null) {
	    preferences = new XMLContext();
	}
	return preferences;
    }

    public void setStylePreferences(XMLContext prefs) {
	preferences = prefs;
    }

    /**
	 * キットがサポートするデータの MIME タイプを返します。このキットは text/xml です。
	 *
	 * @returns モデル
	 */
    public String getContentType() {
	return "text/xml";
    }

    /**
	 * このタイプのエディタに適した、初期化されていないテキスト記憶モデル PlainDocument を作成します。
	 *
	 * @returns モデル
	 */
    public Document createDefaultDocument() {
	return new PlainDocument();
    }

    /**
	 * このキットが作成した任意のモデルのビューを作成するのに適したファクトリを取り出します。
	 *
	 * @returns ビューファクトリ
	 */
    public final ViewFactory getViewFactory() {
	return getStylePreferences();
    }

    XMLContext preferences;
}

XMLContext.java

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Shape;

import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.PlainDocument;
import javax.swing.text.Segment;
import javax.swing.text.StyleContext;
import javax.swing.text.Utilities;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.WrappedPlainView;


public class XMLContext extends StyleContext implements ViewFactory {

	public View create(Element elem) {
		return new XmlView(elem);
	}

	class XmlView extends WrappedPlainView {

		XmlView(Element elem) {
		    super(elem);
		    //this.se
		    //PlainDocument doc =  (PlainDocument) getDocument();
		    //lexer = doc.createScanner();
		    //lexerValid = false;
		}

	        public void paint(Graphics g, Shape a) {
		    super.paint(g, a);
		    //lexerValid = false;
		}

		/**
		 * モデル内の指定された範囲を、選択解除した通常のテキストで描画します。
		 * フォアグラウンドカラーまたは使用不可の色を使ってテキストを描画します。
		 *
		 * @param g グラフィックスコンテキスト
		 * @param x 開始 X 座標 >= 0
		 * @param y 開始 Y 座標 >= 0
		 * @param p0 モデル内の開始位置 >= 0
		 * @param p1 モデル内の終了位置 >= 0
		 * @returns 範囲の終了の X 位置 >= 0
		 * @exception 範囲が無効な場合
		 */
	        protected int drawUnselectedText(Graphics g, int x, int y,int p0, int p1) throws BadLocationException {
	        	Document doc = getDocument();
		    	Segment text = new Segment();
		    	doc.getText(p0, p1 - p0, text);
		    	String lineText = text.toString();

		    	for(int lineMark = 0;lineMark<lineText.length();){  		
		    		Segment token = new Segment();

		    		int start = lineText.indexOf('<',lineMark);
		    		if(start == -1){
		    			start = p1 - p0;
		    		}
		    		g.setColor(Color.BLACK);
		    		doc.getText(p0 + lineMark, start - lineMark, token);
		    		x = Utilities.drawTabbedText(token, x, y, g, this, 0);
		    		
		    		int end = lineText.indexOf('>',start);
		    		if(end == -1){
		    			end = p1 - p0 - 1;
		    		}
		    		g.setColor(Color.BLUE);
		    		doc.getText(p0 + start, end - start + 1, token);
		    		x = Utilities.drawTabbedText(token, x, y, g, this, 0);

		    		lineMark = end + 1;
		    	}
		    	
		    	return x;
	        }
	        //boolean lexerValid;
	    }


}

そしてこんな風に使う。

mTextArea = new JEditorPane();
XMLEditorKit kit = new XMLEditorKit();
mTextArea.setEditorKitForContentType("text/xml", kit);
mTextArea.setContentType("text/xml");

とりあえず動作第一で確認しながら組んでそのままなのであまりスマートなコードじゃないです。一時退避用コメントとかそのままだし!。
現在はxmlのコメント部分の色替えと自動折り返しの制御に挑戦中。

以上、あまりにもEditorKitの詳しい解説をしているところが無いので、
万が一もしかすると他にEditorKitの自作なりテキストのカラーリングなりしようする人のがうっかり見つけちゃったら参考程度になるかもしれないのでメモ。