日別アーカイブ: 2013年8月8日

PMDでCUSTOM RULE

昔のブログから引っ越してきた、2011/08/05当時の記事です

 

参考サイトを見ながらぺちぺちとコーディングルール作りの勉強を開始。

今時点でわかったこと
・PMDではチェック対象ソースがツリー状のオブジェクトに変換される。
・ツリー状のオブジェクトに対してXpathやJavaで条件を記載していくことで
ルールを作成できる
・ルールを継承する時の親はAbstractRuleでなくAbstractJavaRuleになっている
・javaで条件を欠く場合は、AbstractJavaRuleがVisterパターンで作られているので
そのvisitに書いていけばよい
・visitの第一引数は色々な型で用意されている。
型はクラスの宣言を表す型等色々あるみたいだが要調査。
・ルールの呼び出しはリンク先のejbrules.xmlをイメージして準備すればよいらしい。(未実行)

######## 親クラス ######
// import net.sourceforge.pmd.AbstractRule; @deprecated
import net.sourceforge.pmd.AbstractJavaRule; // 4.2.5ではこっち

######## もともとのソース ######
package jp.epea;

public class A001 {
/**
* @param args
*/
public static void main(String[] args) {
if(true){

}

}

}

######## ツリー状のソースオブジェクト(実質XML表現:><はエスケープ) ######

 <?xml version="1.0" encoding="UTF-8"?>
<CompilationUnit beginColumn="1" beginLine="1" comments="[net.sourceforge.pmd.ast.FormalComment@aa415d]" endColumn="3" endLine="15" packageDeclaration="PackageDeclaration">
<PackageDeclaration beginColumn="1" beginLine="1" endColumn="16" endLine="1" packageNameImage="jp.epea">
<Name beginColumn="9" beginLine="1" endColumn="15" endLine="1" image="jp.epea"/>
</PackageDeclaration>
<TypeDeclaration beginColumn="1" beginLine="3" endColumn="1" endLine="15">
<ClassOrInterfaceDeclaration abstract="false" beginColumn="8" beginLine="3" endColumn="1" endLine="15" final="false" image="A001" interface="false" modifiers="1" native="false" nested="false" packagePrivate="false" private="false" protected="false" public="true" static="false" strictfp="false" synchronized="false" transient="false" volatile="false">
<ClassOrInterfaceBody beginColumn="19" beginLine="3" endColumn="1" endLine="15">
<ClassOrInterfaceBodyDeclaration anonymousInnerClass="false" beginColumn="5" beginLine="8" endColumn="5" endLine="13" enumChild="false">
<MethodDeclaration abstract="false" beginColumn="19" beginLine="8" block="Block" endColumn="5" endLine="13" final="false" interfaceMember="false" methodName="main" modifiers="17" native="false" packagePrivate="false" private="false" protected="false" public="true" resultType="ResultType" static="true" strictfp="false" synchronized="false" syntacticallyAbstract="false" syntacticallyPublic="true" transient="false" void="true" volatile="false">
<ResultType beginColumn="19" beginLine="8" endColumn="22" endLine="8" void="true"/>
<MethodDeclarator beginColumn="24" beginLine="8" endColumn="42" endLine="8" image="main" parameterCount="1">
<FormalParameters beginColumn="28" beginLine="8" endColumn="42" endLine="8" parameterCount="1">
<FormalParameter abstract="false" array="true" arrayDepth="1" beginColumn="29" beginLine="8" endColumn="41" endLine="8" final="false" modifiers="0" native="false" packagePrivate="true" private="false" protected="false" public="false" static="false" strictfp="false" synchronized="false" transient="false" typeNode="Type" varargs="false" volatile="false">
<Type array="true" arrayDepth="1" beginColumn="29" beginLine="8" endColumn="36" endLine="8" typeImage="String">
<ReferenceType array="true" arrayDepth="1" beginColumn="29" beginLine="8" endColumn="36" endLine="8">
<ClassOrInterfaceType beginColumn="29" beginLine="8" endColumn="34" endLine="8" image="String"/>
</ReferenceType>
</Type>
<VariableDeclaratorId array="false" arrayDepth="0" beginColumn="38" beginLine="8" endColumn="41" endLine="8" exceptionBlockParameter="false" image="args" typeNameNode="ReferenceType" typeNode="Type"/>
</FormalParameter>
</FormalParameters>
</MethodDeclarator>
<Block beginColumn="44" beginLine="8" endColumn="5" endLine="13">
<BlockStatement allocation="false" beginColumn="9" beginLine="9" endColumn="9" endLine="11">
<Statement beginColumn="9" beginLine="9" endColumn="9" endLine="11">
<IfStatement beginColumn="9" beginLine="9" endColumn="9" endLine="11">
<Expression beginColumn="12" beginLine="9" endColumn="15" endLine="9">
<PrimaryExpression beginColumn="12" beginLine="9" endColumn="15" endLine="9">
<PrimaryPrefix beginColumn="12" beginLine="9" endColumn="15" endLine="9">
<Literal beginColumn="12" beginLine="9" charLiteral="false" endColumn="15" endLine="9" floatLiteral="false" intLiteral="false" singleCharacterStringLiteral="false" stringLiteral="false">
<BooleanLiteral beginColumn="12" beginLine="9" endColumn="15" endLine="9" true="true"/>
</Literal>
</PrimaryPrefix>
</PrimaryExpression>
</Expression>
<Statement beginColumn="17" beginLine="9" endColumn="9" endLine="11">
<Block beginColumn="17" beginLine="9" endColumn="9" endLine="11"/>
</Statement>
</IfStatement>
</Statement>
</BlockStatement>
</Block>
</MethodDeclaration>
</ClassOrInterfaceBodyDeclaration>
</ClassOrInterfaceBody>
</ClassOrInterfaceDeclaration>
</TypeDeclaration>
</CompilationUnit>

エクリプスプラグイン

昔のブログから引っ越してきた、2011/07/29当時の記事です

ThinkITの記事を見つつ(リンク切れ)HelloWorldコマンドプラグインでベースを作成。

やるべきことは技術的には

  1. 1.eclipse上の選択フォルダを取得する
  2. ファイル一覧を取得する
  3. 既存ファイルを削除する
  4. ファイルをコピーする

ぐらい。

1.eclipse上の選択フォルダを取得する

execute(ExecutionEvent event) { //で ISelection currentSelections = HandlerUtil.getCurrentSelection(event); // 選択取得

//  ISelection 継承したインターフェースは現在はIStructuredSelectionだけ

// 配下はiterator()で回すとObject型でとれる
IStructuredSelection sSelection = (IStructuredSelection) currentSelections;
for (Iterator iterator = sSelection.iterator(); iterator.hasNext();) {

// オブジェクトをキャストして選択を取得
IFolder selectedFolder = (IFolder) selectedObj;

とれるものは下のもの。
/**
* Type constant (bit mask value 1) which identifies file resources.
*
* @see IResource#getType()
* @see IFile
*/
public static final int FILE = 0x1;

/**
* Type constant (bit mask value 2) which identifies folder resources.
*
* @see IResource#getType()
* @see IFolder
*/
public static final int FOLDER = 0x2;

/**
* Type constant (bit mask value 4) which identifies project resources.
*
* @see IResource#getType()
* @see IProject
*/
public static final int PROJECT = 0x4;

ルートはIResourceの方にあるけどC:\のようなファイルシステム上のルートを表すので
ワークベンチの選択では飛んでこない(はず。。)
/**
* Type constant (bit mask value 8) which identifies the root resource.
*
* @see IResource#getType()
* @see IWorkspaceRoot
*/
public static final int ROOT = 0x8;

eclipse起動直後の何も選択していない状態はnullでなく空のイテレーターだった。

2.ファイル一覧を取得する

IFolder extends IContainer extends IResourceで
(意味はあとで見てもわかるはず。親の親)
IContainer.members()で直下のリソース(フォルダ内なのでフォルダかファイル)が取得できる
直下のみなので配下一覧を取得する場合は再帰処理

private void getFileList(IFolder tartgetFolder, List fileList) throws CoreException {
{
assert tartgetFolder != null;
assert fileList != null;
}

IResource[] members = tartgetFolder.members();
for(IResource currentResource : members ){
if( currentResource.getType() == IResource.FOLDER ) {
getFileList( (IFolder)currentResource, fileList);

} else if(currentResource.getType() == IResource.FILE ){
IFile currentFile = (IFile)currentResource;
if(isCopyTarget(currentFile)) fileList.add( currentFile );
}
}
}

3.既存ファイルを削除する

currentFile.delete(true, null);
で削除。
一個目の引数はファイルシステムと同期できていない場合に削除するか
二個目の引数はモニター(?)を使うためのもの。進捗状況ビュー???

private void deleteOldImages(IContainer tartgetFolder) throws CoreException {

IResource[] members = tartgetFolder.members();
for(IResource currentResource : members){
if( currentResource.getType() != IResource.FILE ) continue;// リソースの種類
IFile currentFile = (IFile)currentResource;
if( this.isRemoveTarget(currentFile) )
currentFile.delete(true, null);
}
}

4.ファイルをコピーする

コピーしたいファイルオブジェクトの、コピーメソッドを呼び出す。
引数のバージョンはあるけど使ったのは以下。
currentFile.copy(targerFolderPath.append(sbFileName.toString()), true, null);

第一引数はファイルのパスオブジェクト(IPath )
パスオブジェクトは
IResource.getFullPathでプロジェクト名を起点としたパス
IResource.getLocationはローカルのファイルシステムを起点としたパス(c:\~)

第二引数はファイルシステムと同期できていない場合に削除するか
第三引数はモニター(?)を使うためのもの。進捗状況ビュー???

IResource.getFullPath
IResource.getLocation

※↓の定数はAPIのでなく自分で宣言したもの
private void copyTargetFileList(IContainer tartgetFolder, List targetFileList) throws CoreException {

StringBuffer sbFileName = new StringBuffer();

IPath targerFolderPath = tartgetFolder.getFullPath();

for( IFile currentFile: targetFileList ){
String[] segmentArray = currentFile.getFullPath().removeFirstSegments(SEGMENT_INDEX_DRAWABLE + 1 ).segments();
for( int arrayIndex = 0 ; arrayIndex < segmentArray.length; arrayIndex++){
sbFileName.append(segmentArray[arrayIndex]);
if( arrayIndex != segmentArray.length -1 ) sbFileName.append(FILE_DELIMITER);
}

currentFile.copy(targerFolderPath.append(sbFileName.toString()), true, null);

sbFileName.delete(0, sbFileName.length());
}

}

Fileに色々な属性があるので、この通りに動かないことも多いけどベースはこんな感じで。

 

 

adimg_subdirプラグイン その2

昔のブログから引っ越してきた、2011/07/28当時の記事です

昨日のブログで書いたアンドロイドの画像ファイルをサブフォルダで管理できるようにするプラグイン。
大まかな動作は、drawableのサブフォルダ(直下のみ)を選んでプラグインを実行すると、
「フォルダ名をアンスコで結合をしたファイル名」のファイルをdrawableに出力するというもの。

動かしてみると、svnファイルをコピーしてしまったり消してしまったり。。。
他にもアイコンファイルはデフォルトでAndroidManifest.xmlにのっているからそのまま出力すると名前が一致しなくなるなぁ。

alpha版はソース付きでアップした(アップしたリンク先無くなってました)ので興味ある人は覗いてみてやってください。

(出力イメージ)
res
│
├─drawable-hdpi
│ │ a_ba_icon.png
│ │ a_ba_mountpoint.jpg
│ │ a_ba_mypoint.jpg
│ │ a_ba_on.png
│ │ a_icon.png
│ │ a_mountpoint.jpg
│ │ a_mt.bmp
│ │ a_mypoint.jpg
│ │ ba_icon.png
│ │ ba_mountpoint.jpg
│ │ ba_mt.bmp
│ │ ba_mypoint.jpg
│ │ ba_on.png
│ │ ic_icon.png
│ │
│ ├─a
│ │ │ icon.png
│ │ │ mountpoint.jpg
│ │ │ mt.bmp
│ │ │ mypoint.jpg
│ │ │
│ │ └─ba
│ │ icon.png
│ │ mountpoint.jpg
│ │ mypoint.jpg
│ │ on.png
│ │
│ ├─ba
│ │ icon.png
│ │ mountpoint.jpg
│ │ mt.bmp
│ │ mypoint.jpg
│ │ on.png
│ │
│ └─ic
│ icon.png
│
├─drawable-ldpi
│ │ ba_icon.png
│ │ ba_mountpoint.jpg
│ │ ba_mt.bmp
│ │ ba_mypoint.jpg
│ │ ic_icon.png
│ │
│ ├─ba
│ │ icon.png
│ │ mountpoint.jpg
│ │ mt.bmp
│ │ mypoint.jpg
│ │
│ └─ic
│ icon.png

プラグイン Androidの画像フォルダ(res/drawable)用作業ファイル配置

昔のブログから引っ越してきた、2011/07/27当時の記事です

アンドロイド画像管理用サブフォルダプラグイン
前のブログに書いたけど、androidはサブフォルダに分割管理した画像ファイルは直接リソースとして使えないということなので、サブフォルダ管理した状態でres/drawableに反映できるようにするプラグインを作ることにした。
エクリプスのプラグイン作るのは久々だけど大した機能作る気はないのですぐにできるかな??

動作概要
アイコンの作業ファイル配置先資料はあったけど、画像ファイルのワークに関しては見つからないなぁ。

なんとなくiconに似せた雰囲気で
1.drawable*の下にサブフォルダが掘ってある状態で、
2.サブフォルダを選んでプラグイン起動すると
2フォルダの名称とファイル名をアンスコ結合したファイルをdrawable*に吐き出す
ようにするか。

技術メモリンク

Androidの画像フォルダ(res/drawable)

昔のブログから引っ越してきた、2011/07/27当時の記事です

解像度や向きごとに命名規約に従ったフォルダに、それぞれの解像度や向きごと用の画像を入れておけば表示時に自動で切り替えてくれる。
(参考:日本語
(参考:英語=本家

注意しなくてはいけないのは、読み込まれるリソースはフォルダの一階層目だけ。
つまり、下のように目的に応じたフォルダ分けができない・・・・
res/drawable/mount/mount01.bmp
res/drawable/river/river01.bmp
(参考:android ml)

別フォルダ管理プラグイン作るか。。。

WebSocketにてJSON形式のデータをやり取りしてみる(Glassfish 4)

Glassfish4とHTML間でJSON形式のデータをWebsocketにてやり取りしてみたのでメモ。

大まかな動きは以下

  1. HTMLからJSONで入力テキストと現在日時を送信。
  2. GlassfishのWebsocketで受信(&Decode)
  3. 受け取ったデータに情報(とりあえずユーザ情報代わりのセッションID)を付与し繋がっているクライアント(HTML)全体にJSON形式で返信
  4. HTMLで受け取ったデータを表示
(html->glassfishのデータ形式)
{"message":"メッセージ","messageTime":時間(ミリ秒)}

(glassfish->htmlのデータ形式)
{"message":"メッセージ","sessionId":"セッションID","messageTime":"HH:mm:ss"}

斜体は変数

Maven

必要な依存関係を追加。(動作確認はeclipse上のM2E)

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- 8/10追記 javaee-web-apiの中に入っていたもので足りた -->
        <!-- Websocket (JSR-356) -->
        <dependency>
            <groupId>javax.net.websocket</groupId>
            <artifactId>javax.net.websocket-api</artifactId>
            <version>1.0-b06</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>org.glassfish.websocket</groupId>
            <artifactId>websocket-api</artifactId>
            <version>0.2</version>
            <scope>provided</scope>
        </dependency>
        <!-- JSON Processing (JSR-353) を使う場合-->
        <dependency>
            <groupId>javax.json</groupId>
            <artifactId>javax.json-api</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.json</artifactId>
            <version>1.0.2 </version>
        </dependency>
    </dependencies>

画面

  1. new WebSocket(arg)のargで接続先指定をしてWebSocketオブジェクトを取得する
  2. 接続先は@ServerEndpointをつけたサーバサイドのPOJOに指定したもの。
  3. WebSocketのsendで送信、onmessageで受信
  4. Websocketで送られてきたデータはsocket.onmessageで渡される引数(今回はmessage)のdataに入っている。
  5. 取り出してからの扱いは普通のJSON。
 <!DOCTYPE html>
<html>
<head>
<title>JsonのWebSocketテスト</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script
    src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script type="text/javascript">
    var socket;
    $(document).ready(
            function() {
                var host = "ws://localhost:8080/first/hellojson/";
                socket = new WebSocket(host);

                socket.onmessage = function(message) {
                    try {
                        var wsRes = $.parseJSON(message.data);
                        $('#log').append(
                                wsRes.messageTime +
                                "(送信者=" + wsRes.sessionId + "):" + 
                                wsRes.message + "<br/>");
                    } catch (e) {
                        alert(e);
                        return;
                    }
                }

                $('#send').click(function() {
                    var obj = new Object();
                    obj.message = $('#msg').val();
                    obj.messageTime = new Date().getTime();
                    var jsonString = JSON.stringify(obj);
                    socket.send(jsonString);
                    $('#msg').val('');
                })

            });
</script>
</head>
<body>
    <h1>JsonのWebSocketテスト</h1>
    <div id="log"></div>
    <input id="msg" type="text" />
    <button id="send">送信</button>
</body>
</html>

WebSocket用のPOJO(GlassFish側)

  1. @ServerEndpointをつけたPOJOでWebSocket用クラス(サーバエンドポイント)を作る
  2. パスはvalueで指定。自分の環境ではコンテキストがfirstなのでfirst/hellojson/がサーバエンドポイントのアドレス
  3. @onMessageが繋がっている(クライアントサイドの)エンドポイントから呼ばれたときに動く個所
  4. decodersでクラスを指定すると、やってきたメッセージが指定されたクラスでデコードされて渡される。
  5. encodersでクラスを指定すると、指定したクラスでエンコードしたデータを、繋がっているエンドポイントに渡せる。
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import jp.co.epea.first.json.TestData;
import jp.co.epea.first.json.TestDecoder;
import jp.co.epea.first.json.TestEncoder;

@ServerEndpoint(value = "/hellojson/", 
decoders = { TestDecoder.class }, encoders = { TestEncoder.class })
public class HelloJson {

    static Set<Session> sessions = Collections
            .synchronizedSet(new HashSet<Session>());

    @OnMessage
    public void onMessage(TestData data, Session sess) {
        System.out.println("Call HelloJson:data[" + data + "]");
        data.setSessionId(sess.getId());
        for (Session s : sessions) {
            s.getAsyncRemote().sendObject(data);
        }
    }

    @OnOpen
    public void open(Session sess) {
        System.out.println("開始します:" + sess.getId());
        sessions.add(sess);
    }

    @OnClose
    public void close(Session sess) {
        System.out.println("終了します:" + sess.getId());
        sessions.remove(sess);
    }

    @OnError
    public void error(Session sess, Throwable t) {
        System.out.println("エラーです:" + sess.getId());
        t.printStackTrace();
    }
}

decodersのクラス

  1. javax.websocket.DecoderのText<T>をimplementsする。(Text形式であるJSONの場合)
  2. <T>に入るのはエンドポイントのOnMessageに渡したい型
  3. willDecodeメソッドがデコードできるかどうか判定。
  4. バリデーションとかはwillDecodeでなくOnMessageでやるべきがどうかは未調査。(未実装)
  5. decodeメソッドで返却するオブジェクトを生成しreturnする。
  6. JSON Processingを使っているが何を使ってObject化してもOK
import java.io.StringReader;

import javax.json.Json;
import javax.json.JsonException;
import javax.json.JsonObject;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;

public class TestDecoder implements Decoder.Text<TestData> {

@Override
public void destroy() {
System.out.println("TestDecoder#destroy");
}

@Override
public void init(EndpointConfig arg0) {
System.out.println("TestDecoder#init");
}

@Override
public TestData decode(String inputString) throws DecodeException {
JsonObject jsonObject = Json.createReader(new StringReader(inputString)).readObject();
return  new TestData(jsonObject);
}

/*
* Answer whether the given String can be decoded into an object of type
* T.だそう 入力チェックもここでやるのがよい?
*/
@Override
public boolean willDecode(String inputString) {
try {
Json.createReader(new StringReader(inputString)).readObject();
return true;
} catch (JsonException ex) {
ex.printStackTrace();
return false;
}
}
}

encodersのクラス

  1. javax.websocket.EncoderのText<T>をimplementsする。(Text形式であるJSONの場合)
  2. <T>に入るのはエンドポイントのOnMessageで呼ぶsendObjectの引数(返す元ネタ)
  3. encodeメソッドで返却するStringを生成しreturnする。
  4. JSON Processingを使っているが何を使ってString化してもOK
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.json.Json;
import javax.json.JsonObject;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

public class TestEncoder implements Encoder.Text {

	@Override
	public void init(EndpointConfig paramEndpointConfig) {
		System.out.println("TestEncoder#init");
	}

	@Override
	public void destroy() {
		System.out.println("TestEncoder#destroy");
	}

	@Override
	public String encode(TestData paramData) throws EncodeException {
		JsonObject model = Json.createObjectBuilder()
				   .add("message", paramData.getMessage())
				   .add("sessionId",paramData.getSessionId())
				   .add("messageTime", formatDate(paramData.getMessageTime()))
				   .build();
		return model.toString();
	}

	private String formatDate(long millDate){
		return new SimpleDateFormat("HH:mm:ss").format(new Date(millDate));
	}

}

データクラス

import javax.json.JsonObject;

public class TestData {

    // メッセージ
    private String message;
    // 送信日時
    private long messageTime;
    // sessionId(ユーザ名の変わり)
    private String sessionId;

    public TestData(JsonObject jsonObject) {
        if(jsonObject.containsKey("message")){
            this.message = jsonObject.getString("message");
        }
        if(jsonObject.containsKey("messageTime")){
            this.messageTime = jsonObject.getJsonNumber("messageTime").longValue();
        }
    }

    public void setSessionId(String sessionId) {
        this.sessionId = sessionId;
    }

    String getMessage() {
        return message;
    }

    long getMessageTime() {
        return messageTime;
    }

    String getSessionId() {
        return sessionId;
    }
}

github[https://github.com/epea/test01/tree/WS_JSON]