NteBeans6.9にFindBugsをインストールする。

ソースコードの静的解析を行い、バグの疑いのある箇所を抽出してくれるFindBugsをインストールする方法のメモ。

まずプラグインのアップデートセンターに次のアドレスを追加します。

http://deadlock.netbeans.org/hudson/job/sqe/lastStableBuild/artifact/build/full-sqe-updatecenter/updates.xml


追加後に「カタログの再読み込み」を行うと「Update Center for SQE」という名前の項目が追加されるので、
チェックボックスを選択しインストールします。
途中で署名されていないとメッセージが出ますが気にせず進めます。


インストール終了後、もう一度「カタログの再読み込み」を行うとカテゴリ「Quality」に

PMD
FindBugs
CheckStyle
Dependency Finder

の4つが追加されるので、「FindBugs」を選択してインストールすれば完了です。

mavenで自動デプロイを実行する

mavenTomcatサーバーに自動でデプロイを行えるようにする設定のメモ。
mavenのバージョン:2.2.1
Tomcatのバージョン:7.0.6

まずはTomcatでmanagerの権限設定を行います。
CATALINA_HOME/conf/tomcat-user.xmlをエディタで開き、
ロールにmanager-gui、manager-scriptがなければ追加。
ブラウザ上からログインするユーザーにはロール「manager-gui
mavenで使用するユーザーにはロール「manager-script」を設定します。
私は今回「tomcat」ユーザーに両方のロールを設定しました。
CATALINA_HOME/conf/tomcat-user.xml

<tomcat-users>
  <role rolename="tomcat"/>
  <role rolename="role1"/>
<role rolename="manager-gui"/>
  <role rolename="manager-script"/>
  <role rolename="admin"/>
  <user username="tomcat" password="hogehoge" roles="tomcat,manager-gui,manager-script"/>
  <user username="both" password="xxx" roles="tomcat,role1"/>
  <user username="role1" password="xxx" roles="role1"/>
  <user username="admin" password="xxx" roles="admin"/>
</tomcat-users>

tomcat5.5の場合は「manager-gui」と「manager-script」の代わりに「manager」になります。

次はmavenの「pom.xml」にtomcat-maven-pluginの設定を追加します。
Tomcatサーバーのアドレスが「http://hogehoge:8080/」の場合、
urlは「http://hogehoge:8080/manager/text」になります。
tomcat 5.5の場合は「「http://hogehoge:8080/manager」」
pom.xml

<project>
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>tomcat-maven-plugin</artifactId>
        <configuration>
          <server>tomcat</server>
          <url>http://hogehoge:8080/manager/text</url>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <repositories>
    <repository>
      <releases>
        <enabled>false</enabled>
      </releases>
      <snapshots />
      <id>Maven Snapshots</id>
      <url>http://snapshots.maven.codehaus.org/maven2/</url>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <releases>
        <enabled>false</enabled>
      </releases>
      <snapshots />
      <id>Maven Snapshots</id>
      <url>http://snapshots.maven.codehaus.org/maven2/</url>
    </pluginRepository>
  </pluginRepositories>
</project>


続いて「M2_HOME/conf/settings.xml」にTomcatにアクセスするためのユーザー名、パスワードを設定します。
idの値は「plugin/configuration/server」の値と合わせてください。

settings.xml

<settings>
  <servers>
    <server>
      <id>tomcat</id>
      <username>tomcat</username>
      <password>hogehoge</password>
    </server>
  </servers>
</settings>

これで設定は完了です。
次のコマンドを実行すれば自動でデプロイしてくれるはずです。

mvn tomcat:deploy

XMLContext.java改良版

コメントの着色に対応してみた。
せっかくなので公開しておいてみる。
XMLContext.java

import java.awt.*;
import javax.swing.text.*;


public class XMLContext extends StyleContext implements ViewFactory {

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

	class XmlView extends PlainView {

		XmlView(Element elem) {
		    super(elem);
		}

	        public void paint(Graphics g, Shape a) {
		    super.paint(g, a);
		}
	        
		/**
		 * モデル内の指定された範囲を、選択解除した通常のテキストで描画します。
		 * フォアグラウンドカラーまたは使用不可の色を使ってテキストを描画します。
		 *
		 * @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 {
	        	//if(p0 == 0){
	        		mLastColor = mNormalColor;
	        	//}
	        	Document doc = getDocument();
		    	Segment text = new Segment();
		    	Segment token = new Segment();

		    	doc.getText(p0, p1 - p0, text);
		    	text.setIndex(text.getBeginIndex());

		    	for(;text.getIndex() < text.getEndIndex();text.next()){
		    		
		    		mLastColor = isTagStart(text,mLastColor);

		    		g.setColor(mLastColor);
		    		doc.getText(p0 + text.getIndex() - text.getBeginIndex(), 1, token);
		    		x = Utilities.drawTabbedText(token, x, y, g, this, 0);
		    		
		    		mLastColor = isTagEnd(text,mLastColor);
		    	}
		    	
		    	
		    	return x;
	        }
	        Color mLastColor = Color.BLACK;
	        Color mNormalColor = Color.BLACK;
	    	Color mTagColor = Color.BLUE;
	    	Color mCommentColor = Color.LIGHT_GRAY;
	        
	    	/**
			 * タグまたはコメントの開始を引数のテキストから検出し適切な色を返します。
			 *
			 * @param text テキスト
			 * @param color 現在の色
			 * @returns 色
			 */
	    	final Color isTagStart(Segment text,Color color){
	        	if(text.current() == '<'){
	    			if(text.next() == '!'){
	    				if(text.next() == '-'){
	    					if(text.next() == '-'){
	    						color = mCommentColor;
		    				}
	    					text.previous();
	    				}
	    				text.previous();
	    			} else {
	    				if(color != mCommentColor){
	    					color = mTagColor;
	    				}
	    			}
	    			text.previous();
	    		}
	        	return color;
	        }
	        
	        /**
			 * タグまたはコメントの終了を引数のテキストから検出し適切な色を返します。
			 *
			 * @param text テキスト
			 * @param color 現在の色
			 * @returns 色
			 */
	        final Color isTagEnd(Segment text,Color color){
	        	if(text.current() == '>'){
	        		
	    			if(text.previous() == '-'){
	    				if(text.previous() == '-'){
	    					color = mNormalColor;
		    			}
		    			text.next();
	    				text.next();
	    			} else {
	    				text.next();
	    			}
	    			
	    			if(color != mCommentColor){
	    				color = mNormalColor;
	    			}
	    		}
	        	return color;
	        }
	}
}

簡単なテストしかしてないからバグがあるかも。
折り返し問題はXMLContextのインナークラスであるXmlViewクラスの継承元をWrappedPlainViewからPlainViewクラスに
変更することで一応解決できるようです。
わざわざクラスごと分けてるとは思わなかったのでまったく気づかなかったよ。

テキストカラーリング用の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の自作なりテキストのカラーリングなりしようする人のがうっかり見つけちゃったら参考程度になるかもしれないのでメモ。