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

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

JavaBeanを別のJavaBeanに変換する

JavaBeanを別のJavaBeanに型変換して値をコピーするユーティリティ。DXOを書くのが面倒になったときに使えます。

機能
  • JavaBeanを別のJavaBeanに型変換します。
    • 変換前のクラス、変換後のクラス共にJavaBeanである必要があります。
  • 同じ名前のプロパティをコピーします。
    • 変換元からgetterで取得したプロパティを変換後クラスののsetterを使ってコピーします。
      • このとき、プロパティ値の型も再帰的に変換されます。
    • 変換元にのみあるプロパティはコピーされません。
    • 型はチェックしていません。(エラーになります。)
サンプル

変換するJavaBean。

// 変換元のBean
static class Kitten {
    private String name;
    private boolean isFemale;
    public final String getName () {
        return name;
    }
    public final void setName ( String name ) {
        this.name = name;
    }
    public final boolean isFemale () {
        return isFemale;
    }
    public final void setFemale ( boolean isFemale ) {
        this.isFemale = isFemale;
    }
    public String toString() {
        return getClass().getName() + ":"
            + getName() + ":" + String.valueOf( isFemale() );
    }
}
// 変換後のBean
static class Lion {
    private String name;
    private boolean isFemale;
    public final String getName () {
        return name;
    }
    public final void setName ( String name ) {
        this.name = name;
    }
    public final boolean isFemale () {
        return isFemale;
    }
    public final void setFemale ( boolean isFemale ) {
        this.isFemale = isFemale;
    }
    public String toString() {
        return getClass().getName() + ":"
            + getName() + ":" + String.valueOf( isFemale() );
    }
}

変換のコードは次の通りです。

// 変換ルールを作成。
BeanTransformer.Recipe recipe = new  BeanTransformer.Recipe();
recipe.map( Kitten.class, Lion.class ); // Kitten <-> Lionに相互変換。

// 変換器を作成。
BeanTransformer transformer = new  BeanTransformer( recipe );

Kitten mii = new Kitten();
mii.setFemale( true );
mii.setName( "mii" );

System.out.println( mii );
System.out.println( transformer.transform( mii ) ); // 変換
System.out.println(  transformer.transform(transformer.transform( mii )) ); // 逆変換

実行結果です。

BeanTransform$Kitten:mii:true
BeanTransform$Lion:mii:true
BeanTransform$Kitten:mii:true
ユーティリティのソース
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 指定された変換規則に基づき、JavaBeanを変換する。
 *
 * @version $Revision:$
 * @author  $Author:$
 */
public class BeanTransformer {

    /**
     * 変換規則
     */
    private final Recipe recipe;

    /**
     * コンストラクタ
     * @param recipe 変換規則
     */
    public BeanTransformer(Recipe recipe) {
        this.recipe = recipe;
    }

    /**
     * JavaBeanを変換する
     * @param src 変換元オブジェクト
     * @return 変換後のオブジェクト
     * @throws IllegalArgumentException プロパティの型が一致しない場合。
     * @throws IllegalAccessException プロパティのsetter/getterへのアクセスが許可されない場合。
     * @throws InvocationTargetException setter/getterの実行に失敗した場合。
     * @throws IntrospectionException Beanの情報取得に失敗した場合
     * @throws InstantiationException 変換後オブジェクトのインスタンス化に失敗した場合
     */
    public Object transform ( Object src )
    throws IllegalArgumentException, IllegalAccessException,
    InvocationTargetException, IntrospectionException, InstantiationException {

    if ( src == null ) { return null; }

        Class srcClass = src.getClass();
        Class dstClass = recipe.getDestination( srcClass );

        // 配列やコレクションは別処理。
        if ( src instanceof List ) {
            // List
            Collection srcColection = (Collection) src;
            List tmp = new ArrayList();
            Iterator it = srcColection.iterator();
            while ( it.hasNext() ) {
                tmp.add( transform( it.next() ) );
            }
            return tmp;
        } else if ( src instanceof Set ) {
            // Set
            Collection srcColection = (Collection) src;
            Set tmp = new HashSet();
            Iterator it = srcColection.iterator();
            while ( it.hasNext() ) {
                tmp.add( transform( it.next() ) );
            }
            return tmp;
        } else if ( src instanceof Map ) {
            // Map
            Map srcMap = (Map) src;
            Map tmp = new HashMap();
            Iterator it = srcMap.entrySet().iterator();
            while ( it.hasNext() ) {
                Map.Entry e = ( Map.Entry ) it.next();
                tmp.put( transform( e.getKey() ), transform( e.getValue()) );
            }
            return tmp;
        } else if( srcClass.isArray()) {
            // 配列
            dstClass = recipe.getDestination( srcClass.getComponentType());
            if ( dstClass == null ) { return src; }
            int size =  Array.getLength( src ) ;
            Object dst = Array.newInstance( dstClass, size );
            for ( int i = 0; i < size; i++ ) {
                Object o = transform(Array.get( src, i ));
                Array.set( dst, i, o );
            }
            return dst;
        }

        if ( dstClass == null ) { return src; }

        // プロパティの情報を取得
        Map<String, PropertyDescriptor> srcProperties=
            toPropertyMap(Introspector.getBeanInfo( srcClass ));
        Map<String, PropertyDescriptor> dstProperties=
            toPropertyMap(Introspector.getBeanInfo( dstClass ));

        Object dst = dstClass.newInstance(); // インスタンス作成

        // プロパティの設定
        for ( Map.Entry<String, PropertyDescriptor> entry : srcProperties.entrySet() ) {
            // 対応するプロパティが変換先に存在する場合のみ、設定する。
            PropertyDescriptor srcProperty = entry.getValue();
            PropertyDescriptor dstProperty = dstProperties.get( entry.getKey() );
            if ( srcProperty != null && srcProperty.getReadMethod() != null
               && dstProperty != null && dstProperty.getWriteMethod() != null ) {
                Object value = srcProperty.getReadMethod().invoke( src, new Object[0] );
                value = transform( value );
                dstProperty.getWriteMethod().invoke( dst, new Object[] { value } );
            }
        }
        return dst;
    }

    private static Map<String, PropertyDescriptor> toPropertyMap( BeanInfo info ){
        Map<String, PropertyDescriptor> map =
            new HashMap<String, PropertyDescriptor>();
        for ( PropertyDescriptor p :  info.getPropertyDescriptors() ) {
            map.put( p.getName(), p );
        }
        return map;
    }

    /**
     * 変換規則
     */
    public static class Recipe {
        private final Map<Class, Class> mapping
            = new HashMap<Class, Class>();

        /**
         * コンストラクタ
         */
        public Recipe( ) {}

        /**
         * 変換規則を追加する。(a→bに変換、b→aに変換)
         * @param a クラス
         * @param b クラス
         */
        public void map( Class a, Class b ) {
            this.mapping.put( a, b );
            this.mapping.put( b, a );
        }
        /**
         * 変換後のクラスを取得する。
         * @param source 変換元のクラス
         * @return 変換後のクラス
         */
        public Class getDestination( Class source ) {
            return mapping.get( source  );
        }

    }
}

追記 (2007/08/21)

以下のバグがあったのでなおしました。

  • プロパティにBeanを持つBeanの変換でエラー。子のBeanの型変換が行われない。