    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.fukurou.business;
    017
    018import org.opengion.fukurou.model.DataModel;
    019import org.opengion.fukurou.model.NativeType;
    020
    021import java.util.Map;
    022import java.util.HashMap;
    023import java.util.Arrays;
    024
    025/**
    026 * 業務ロジックを処理するためのテーブルモデルです。
    027 *
    028 * このテーブルモデルでは、オブジェクト生成時に、カラム配列、値配列を元に、内部データを生成し、
    029 * その後は、行の追加や値の変更はできません。
    030 *
    031 * @og.rev 5.1.1.0 (2009/12/01) 新規作成
    032 * @og.group 業務ロジック
    033 *
    034 * @version 5.0
    035 * @author Hiroki Nakamura
    036 * @since JDK1.6,
    037 */
    038public class ArrayTableModel implements DataModel<String> {
    039        private static final String CR = System.getProperty("line.separator");          // 5.6.7.0 (2013/07/27) 追加
    040
    041        private final String[] names;
    042        private final String[][] vals;
    043        private final String[] modTypes;
    044
    045        private Map<Integer,String[]> rtnMap = null;      // 5.6.0.3 (2012/01/24) 変更された値を、書き戻すためのMap<インデックス,値配列>
    046
    047        /**
    048         * 引数に名前配列、値配列を指定したコンストラクター
    049         *
    050         * @param       nms     名前配列
    051         * @param       vs      値２重配列
    052         * @throws  IllegalArgumentException 引数の配列が不正な場合
    053         */
    054        public ArrayTableModel( final String[] nms, final String[][] vs ) {
    055                this( nms, vs, null );
    056        }
    057
    058        /**
    059         * 引数に名前配列、値配列、変更区分配列を指定したコンストラクター
    060         *
    061         * @og.rev 5.6.7.0 (2013/07/27) エラーメッセージを判りやすくする。
    062         * @og.rev 5.7.2.3 (2014/01/31) vsのチェック条件を戻す
    063         * @og.rev 5.7.3.1 (2014/02/14) nmsのチェック条件も戻す
    064         *
    065         * @param       nms     名前配列
    066         * @param       vs      値２重配列
    067         * @param       ms      変更区分の配列
    068         * @throws  IllegalArgumentException 引数の配列が不正な場合
    069         */
    070        public ArrayTableModel( final String[] nms, final String[][] vs, final String[] ms ) {
    071                if( nms == null || nms.length == 0 ) {
    072                        String errMsg = "引数の名前配列に、null は設定できません。";
    073                        throw new IllegalArgumentException( errMsg );
    074                }
    075                // 5.6.7.0 (2013/07/27) エラーメッセージを判りやすくする。
    076                // 5.7.2.3 (2014/01/31) 結果０行でlength=0で通るようなのでvsのエラーチェック条件を戻す。
    077                if( vs == null ) {
    078//      if( vs == null || vs.length == 0 || vs[0] == null || vs[0].length == 0 ) {
    079                        String errMsg = "引数の値配列に、null は設定できません。";
    080                        throw new IllegalArgumentException( errMsg );
    081                }
    082                // 5.7.3.1 (2014/02/14) 5.7.2.3での戻しでは不十分だったのでこちらも戻す
    083                if( vs.length > 0 ) {
    084                        if( vs[0] == null || vs[0].length == 0 || nms.length != vs[0].length ) {
    085//                      if( nms.length != vs[0].length ) {
    086//                              String errMsg = "名前配列と値配列のカラム数が異なります。" ;
    087                                String errMsg = "名前配列と値配列のカラム数が異なります。"  + CR
    088                                                + "   nms   =" + Arrays.toString( nms   )                       + CR
    089                                                + "   vs[0] =" + Arrays.toString( vs[0] ) ;
    090                                throw new IllegalArgumentException( errMsg );
    091                        }
    092                }
    093
    094                int cols = nms.length;
    095                names = new String[cols];
    096                System.arraycopy( nms, 0, names, 0, cols );
    097
    098                int rows = vs.length;
    099                vals = new String[rows][cols];
    100                for( int i = 0; i < rows; i++ ) {
    101                        System.arraycopy( vs[i], 0, vals[i], 0, cols );
    102                }
    103
    104                if( ms != null && ms.length > 0 ) {
    105                        if( vs.length == ms.length ) {
    106                                modTypes = new String[rows];
    107                                System.arraycopy( ms, 0, modTypes, 0, rows );
    108                        }
    109                        else {
    110                                // 5.6.7.0 (2013/07/27) エラーメッセージを判りやすくする。
    111//                              String errMsg = "変更区分を指定する場合、値配列の行数と一致する必要があります。";
    112                                String errMsg = "変更区分を指定する場合、値配列の行数と一致する必要があります。"       + CR
    113                                                + "   変更区分 行数 =" + ms.length            + CR
    114                                                + "   値配列   行数 =" + vs.length ;
    115                                throw new IllegalArgumentException( errMsg );
    116                        }
    117//                      if( vs.length != ms.length ) {
    118//                              String errMsg = "変更区分を指定する場合、値配列の行数と一致する必要があります。";
    119//                              throw new IllegalArgumentException( errMsg );
    120//                      }
    121//                      else {
    122//                              modTypes = new String[rows];
    123//                              System.arraycopy( ms, 0, modTypes, 0, rows );
    124//                      }
    125                }
    126                else {
    127                        modTypes = null;
    128                }
    129        }
    130
    131        /**
    132         * rowで指定された行番号(インデックス番号)に行を追加します。
    133         *
    134         * 値配列をセットする場合は、以下の条件を満たす必要があります。
    135         *   １．行番号は、0〜(rowCount-1) の範囲
    136         *   ２．値配列は、not null、 かつ １件以上
    137         *   ３．値配列の個数は、内部カラム数と同じ
    138         *
    139         * ここで登録した値は、内部の値配列と別管理されますので、セット後に、再びゲットしても
    140         * ここでセットした値を取り出すことはできません。
    141         * また、同じ行番号でセットした場合は、後でセットした値が有効です。
    142         *
    143         * ※ インデックス(row)とは、このArrayTableModel に持つ vals 配列の行のインデックスです。
    144         * よって、オリジナルのDBTableModelの行番号ではありません。
    145         *
    146         * @og.rev 5.6.0.3 (2012/01/24) 変更された値を、書き戻す機能を追加します。
    147         *
    148         * @param   vals  配列値
    149         * @param   row   追加するインデックス
    150         * @throws      IllegalArgumentException 引数が１，２，３の条件を満たさない場合。
    151         */
    152        public void setValues( final String[] vals, final int row ) {
    153//              throw new RuntimeException( "このクラスでは、setValuesメソッドはサポートされていません" );
    154                if( row < 0 || row > getRowCount() ) {
    155                        String errMsg = "引数のインデックスは、0〜" + (getRowCount()-1) + " の間で指定してください。index=[" + row + "]";
    156                        throw new IllegalArgumentException( errMsg );
    157                }
    158                else if( vals == null || vals.length == 0 ) {
    159                        String errMsg = "引数の値配列に、null、または 0件配列は指定できません。index=[" + row + "]";
    160                        throw new IllegalArgumentException( errMsg );
    161                }
    162                else if( vals.length != names.length ) {
    163                        String errMsg = "引数の値配列の個数と、内部カラム数が一致しません。"
    164                                                + " index=[" + row + "] : 引数個数=[" + vals.length + "] != 内部カラム数=[" + names.length + "]";
    165                        throw new IllegalArgumentException( errMsg );
    166                }
    167
    168                if( rtnMap == null ) { rtnMap = new HashMap<Integer,String[]>(); }
    169
    170                int cols = vals.length;
    171                String[] newVals = new String[cols];
    172                System.arraycopy( vals, 0, newVals, 0, cols );
    173
    174                rtnMap.put( Integer.valueOf( row ) , newVals );
    175        }
    176
    177        /**
    178         * BizLogicで、データが変更された場合は、このMapで値の配列を返します。
    179         * Mapのキーは、インデックス(row)のIntegerオブジェクトです。値は、設定された String配列です。
    180         * なにも変更がされていなければ、null を返します。
    181         *
    182         * ※ インデックス(row)とは、このArrayTableModel に持つ vals 配列の行のインデックスです。
    183         * よって、オリジナルのDBTableModelの行番号ではありません。
    184         *
    185         * @og.rev 5.6.0.3 (2012/01/24) 変更された値を、書き戻すためのMap&lt;インデックス,値配列&gt; を返します。
    186         *
    187         * @return      書き戻すためのMap＜インデックス,値配列＞
    188         * @see AbstractBizLogic#isRequireTable()
    189         */
    190        public Map<Integer,String[]> getModifyVals() {
    191                return rtnMap;
    192        }
    193
    194        /**
    195         * カラム名に対応する カラム番号を返します。
    196         *
    197         * 特殊なカラムが指定された場合は、負の値を返します。
    198         * 例えば、[KEY.カラム名]、[I]、[ROW.ID] など、特定の負の値を返します。
    199         * また、カラム名が元のデータモデルに存在しない場合も、負の値か、
    200         * Exception を返します。負の値なのか、Exception なのかは、
    201         * 実装に依存します。
    202         *
    203         * @param       columnName      値が参照されるカラム名
    204         *
    205         * @return  指定されたセルのカラム番号。存在しなければ、-1
    206         * @throws  IllegalArgumentException 引数のカラム名が null の場合
    207         */
    208        public int getColumnNo( final String columnName ) {
    209                if( columnName == null ) {
    210                        String errMsg = "引数のカラム名に、null は設定できません。";
    211                        throw new IllegalArgumentException( errMsg );
    212                }
    213
    214                int address = -1;
    215                for( int i = 0; i < names.length; i++ ) {
    216                        if( columnName.equalsIgnoreCase( names[i] ) ) {
    217                                address = i;
    218                                break;
    219                        }
    220                }
    221
    222                return address;
    223        }
    224
    225        /**
    226         * カラム名配列に対応する カラム番号配列を返します。
    227         *
    228         * これは、#getColumnNo( String ) に対する 複数のカラム名を検索した
    229         * 場合と同じです。
    230         *
    231         * @param       clmNms  値が参照されるカラム名配列
    232         *
    233         * @return  指定されたセルのカラム番号配列。
    234         */
    235        public int[] getColumnNos( final String[] clmNms ) {
    236                if( clmNms == null ) { return new int[0]; }
    237
    238                int[] clmNos = new int[clmNms.length];
    239                for( int j = 0; j < clmNms.length; j++ ) {
    240                        int address = -1;
    241                        for( int i = 0; i < names.length; i++ ) {
    242                                if( clmNms[j].equalsIgnoreCase( names[i] ) ) {
    243                                        address = i;
    244                                        break;
    245                                }
    246                        }
    247                        clmNos[j] = address;
    248                }
    249
    250                return clmNos;
    251        }
    252
    253        /**
    254         * カラム名配列を返します。
    255         *
    256         * @return      カラム名配列
    257         */
    258        public String[] getNames() {
    259                return names.clone();
    260        }
    261
    262        /**
    263         * row にあるセルの属性値を配列で返します。
    264         *
    265         * @param   row     値が参照される行
    266         *
    267         * @return  指定されたセルの属性値配列
    268         */
    269        public String[] getValues( final int row ) {
    270                return vals[row].clone();
    271        }
    272
    273        /**
    274         * row および clm にあるセルの属性値をStringに変換して返します。
    275         *
    276         * @param   row     値が参照される行
    277         * @param   clm     値が参照される列
    278         *
    279         * @return  指定されたセルの値
    280         *
    281         */
    282        public String getValue( final int row, final int clm ) {
    283                return vals[row][clm];
    284        }
    285
    286        /**
    287         * row および clm にあるセルの属性値をStringに変換して返します。
    288         *
    289         * @param   row     値が参照される行
    290         * @param   clm     値が参照される列(キー)
    291         *
    292         * @return  指定されたセルの値
    293         *
    294         */
    295        public String getValue( final int row, final String clm ) {
    296                return vals[row][getColumnNo( clm )];
    297        }
    298
    299        /**
    300         * データテーブル内の行の数を返します。
    301         *
    302         * @return  モデルの行数
    303         *
    304         */
    305        public int getRowCount() {
    306                return vals.length;
    307        }
    308
    309        /**
    310         * row 単位に変更されたタイプ(追加/変更/削除)を返します。
    311         * タイプは始めに一度登録するとそれ以降に変更はかかりません。
    312         * つまり、始めに 追加で作成したデータは、その後変更があっても追加のままです。
    313         * なにも変更されていない場合は, ""(ゼロストリング)を返します。
    314         *
    315         * @param   row     値が参照される行
    316         *
    317         * @return  変更されたタイプの値
    318         */
    319        public String getModifyType( final int row ) {
    320                return modTypes == null ? "" : modTypes[row];
    321        }
    322
    323        /**
    324         * clm のNativeタイプを返します。
    325         * Nativeタイプはorg.opengion.fukurou.model.NativeTypeで定義されています。
    326         *
    327         * @og.rev 5.1.8.0 (2010/07/01) NativeType#getType(String) のメソッドを使用するように変更。
    328         *
    329         * @param  clm      値が参照される列
    330         *
    331         * @return Nativeタイプ
    332         * @see org.opengion.fukurou.model.NativeType
    333         */
    334        public NativeType getNativeType( final int clm ) {
    335//              return StringUtil.getNativeType( vals[0][clm] );
    336                return NativeType.getType( vals[0][clm] );
    337        }
    338
    339        /**
    340         * このオブジェクトの文字列表記を返します。
    341         * デバッグ用です。
    342         *
    343         * @og.rev 5.6.7.0 (2013/07/27) 新規追加
    344         *
    345         * @return 文字列表現
    346         * @see java.lang.Object#toString()
    347         */
    348        @Override
    349        public String toString() {
    350                StringBuilder buf = new StringBuilder();
    351
    352                buf.append( "NAMES=" ).append( Arrays.toString( names ) ).append( CR )
    353                        .append( " COL_LEN=" ).append( (names != null) ? names.length : -1 ).append( CR )
    354                        .append( " ROW_LEN=" ).append( (vals  != null) ? vals.length  : -1 ).append( CR ) ;
    355
    356                return buf.toString();
    357        }
    358}