無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

JavaBeanをMapに変換するユーティリティ

JavaBeanをMapに変換したり復元したりするユーティリティを書きました。探せばいっぱいありそうなのであれだけど。

使い方

適当なJavaBeanがあるとして、

// テスト用のJavaBean
public static class Kitten {
    private String name;
    private int age;
    
    public Kitten() {}
    public Kitten( String name, int age ) {
        this.name = name;
        this.age = age;
    }
    
    public final String getName () {
        return name;
    }
    public final void setName ( String name ) {
        this.name = name;
    }
    public final int getAge () {
        return age;
    }
    public final void setAge ( int age ) {
        this.age = age;
    }
    @Override
    public String toString() {  
        return new StringBuilder( name ).append( ":" ).append( age ).toString();
    }
}

こんな感じで使えます。

Kitten mii = new Kitten("mii", 2);
System.out.println( mii.toString() ); // mii:2

// Mapに変換
Map<Property<Kitten,?>,Object> map = Properties.toMap( mii );
for ( Map.Entry<Property<Kitten,?>,Object> e : map.entrySet() ) {
    System.out.println( e.getKey().getName() + ":" + e.getValue() );
}

// 復元
Kitten mii2 = Properties.fromMap( map );
System.out.println( mii2.toString() );  // mii:2

// 値を取得
System.out.println( "\n---get" );
Property<Kitten, String> name = 
    new Property<Kitten,String>( "name", Kitten.class, String.class );
System.out.println( map.get( name ) );  // mii
System.out.println( name.get( mii ) );  // mii

// 値を設定
System.out.println( "\n---set" );
// プロパティ経由でモデルの値を更新
name.set( mii, "tora" );
System.out.println( mii.toString() ); // tora:2

// mapの値をいじる。
map.put( name, "kuro" );
mii2 = Properties.fromMap( map );
System.out.println( mii2.toString() ); // kuro:2

実行結果です。

mii:2
age:2
name:mii
mii:2

---get
mii
mii

---set
tora:2
kuro:2

実装

以下の通り。

Properties.java

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * JavaBeanをMapに変換するユーティリティ
 *
 * @version $Revision:$
 * @author  $Author:$
 */
public class Properties {

    /**
     * モデルを解析して、プロパティと値の{@link Map}に変換します。
     * @param <M> モデルの型
     * @param model モデル
     * @return プロパティと値の{@link Map}
     * 
     * @throws IntrospectionException 
     * @throws NoSuchMethodException 
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws SecurityException 
     * @throws IllegalArgumentException 
     */
    public static final <M> Map<Property<M,?>, Object> toMap( M model ) 
    throws IntrospectionException, IllegalArgumentException, SecurityException, 
    IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        
        Map<Property<M,?>, Object> map = new HashMap<Property<M,?>, Object>();
        BeanInfo info = Introspector.getBeanInfo( model.getClass() );
        PropertyDescriptor[] pds = info.getPropertyDescriptors();
        for ( PropertyDescriptor pd : pds ) {
            if ( "class".equals( pd.getName() ) ) {
                continue; // getClassは無視。
            }
            if ( pd.getWriteMethod() == null ) {
                throw new IllegalArgumentException( "setter is not exist. property=" + pd.getName());
            }
            if ( pd.getReadMethod() == null ) {
                throw new IllegalArgumentException( "getter is not exist. property=" + pd.getName());
            }
            Property<M, Object> p = new Property<M, Object>( pd.getName(), 
                (Class<M>)model.getClass(), (Class<Object>) pd.getPropertyType() );
            map.put( p, p.get( model ) );
        }
        return map;
    }
    
    /**
     * プロパティと値の{@link Map}からモデルを復元します。
     * 
     * @param <M> モデルの型
     * @param map プロパティと値の{@link Map}
     * @return モデル
     * 
     * @throws IllegalAccessException 
     * @throws InstantiationException 
     * @throws InvocationTargetException 
     * @throws NoSuchMethodException 
     * @throws IllegalArgumentException 
     * @throws SecurityException 
     */
    public static final <M> M fromMap( Map<Property<M,?>, Object> map ) 
    throws InstantiationException, IllegalAccessException, SecurityException, 
    IllegalArgumentException, NoSuchMethodException, InvocationTargetException  {
        
        if ( map.isEmpty() ) {
            throw new IllegalArgumentException("empty map.");
        }
        Property<M, ?> p = map.keySet().iterator().next();
        M model = p.getModelType().newInstance();
        for ( Entry<Property<M, ?>, Object> e : map.entrySet() ) {
            ( (Property<M,Object>) e.getKey() ).set( model, e.getValue() );
        }
        return model;
    }
    
}

Property.java。CompareUtilはこれ

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * プロパティ
 * 
 * @param <M> モデルの型
 * @param <V> 値の型
 *
 * @version $Revision:$
 * @author  $Author:$
 */
public class Property<M, V> {

    private final Class<M> modelType;
    private final Class<V> valueType;
    private final String name;
    
    /**
     * コンストラクタ
     * @param name プロパティ名
     * @param modelType モデルの型
     * @param valueType 値の型
     */
    public Property( 
        String name, 
        Class<M> modelType,
        Class<V> valueType ) {
        this.name = name;
        this.modelType = modelType;
        this.valueType = valueType;
    }
    
    /**
     * プロパティ名を取得する
     * @return プロパティ名
     */
    public String getName() {
        return this.name;
    }
    /**
     * モデルの型を取得する
     * @return モデルの型
     */
    public Class<M> getModelType() {
        return this.modelType;
    }
    /**
     * 値の型を取得する
     * @return 値の型
     */
    public Class<V> getValueType() {
        return this.valueType;
    }
    /**
     * プロパティ値を取得する。
     * @param model モデル
     * @return プロパティ値
     * 
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws IllegalArgumentException 
     * @throws NoSuchMethodException 
     * @throws SecurityException 
     */
    public final V get( M model ) 
    throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, 
            SecurityException, NoSuchMethodException {
        Method read = model.getClass().getMethod( "get" + toMethodName(getName()) );
        return (V) read.invoke( model );
    }
    /**
     * プロパティ値を設定する。
     * @param model モデル
     * @param value プロパティ値
     * 
     * @throws NoSuchMethodException 
     * @throws SecurityException 
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws IllegalArgumentException 
     */
    public final void set( M model, V value ) 
    throws SecurityException, NoSuchMethodException, IllegalArgumentException, 
            IllegalAccessException, InvocationTargetException {
        Method write = model.getClass().getMethod( "set" + toMethodName(getName()), getValueType() );
        write.invoke( model, value );        
    }
    
    private static final String toMethodName( String name ) {
        StringBuilder sb = new StringBuilder();
        sb.append( name.substring( 0, 1 ).toUpperCase() );
        sb.append( name.substring( 1 ) );
        return sb.toString();
    }
    
    /* 継承元のクラスのJavaDocを参照 */
    public boolean equals ( Object obj ) {
        if ( obj == null ) { return false; }
        if ( obj instanceof Property<?, ?> ) {
            Object[] that = getValues((Property<?, ?>) obj);
            return CompareUtil.equalsSeq(that, getValues(this));
        }
        return false;
    }
    /* 継承元のクラスのJavaDocを参照 */
    public int hashCode () {
        return CompareUtil.hashCodeSeq(getValues(this));
    }
    private static Object[] getValues(Property<?, ?> v) {
        return new Object[] {
            v.getName(),
            v.getValueType(),
            v.getModelType()
        };
    }    
}