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      値2重配列
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      値2重配列
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) 結果0行でlength=0で通るようなのでvsのエラーチェック条件を戻す。
077                if( vs == null ) {
078                        String errMsg = "引数の値配列に、null は設定できません。";
079                        throw new IllegalArgumentException( errMsg );
080                }
081                // 5.7.3.1 (2014/02/14) 5.7.2.3での戻しでは不十分だったのでこちらも戻す
082                // 6.0.0.1 (2014/04/25) These nested if statements could be combined
083                if( vs.length > 0 && ( vs[0] == null || vs[0].length == 0 || nms.length != vs[0].length ) ) {
084                        String errMsg = "名前配列と値配列のカラム数が異なります。"  + CR
085                                        + "   nms   =" + Arrays.toString( nms   )                       + CR
086                                        + "   vs[0] =" + Arrays.toString( vs[0] ) ;
087                        throw new IllegalArgumentException( errMsg );
088                }
089
090                int cols = nms.length;
091                names = new String[cols];
092                System.arraycopy( nms, 0, names, 0, cols );
093
094                int rows = vs.length;
095                vals = new String[rows][cols];
096                for( int i = 0; i < rows; i++ ) {
097                        System.arraycopy( vs[i], 0, vals[i], 0, cols );
098                }
099
100                if( ms != null && ms.length > 0 ) {
101                        if( vs.length == ms.length ) {
102                                modTypes = new String[rows];
103                                System.arraycopy( ms, 0, modTypes, 0, rows );
104                        }
105                        else {
106                                // 5.6.7.0 (2013/07/27) エラーメッセージを判りやすくする。
107                                String errMsg = "変更区分を指定する場合、値配列の行数と一致する必要があります。"       + CR
108                                                + "   変更区分 行数 =" + ms.length            + CR
109                                                + "   値配列   行数 =" + vs.length ;
110                                throw new IllegalArgumentException( errMsg );
111                        }
112                }
113                else {
114                        modTypes = null;
115                }
116        }
117
118        /**
119         * rowで指定された行番号(インデックス番号)に行を追加します。
120         *
121         * 値配列をセットする場合は、以下の条件を満たす必要があります。
122         *   1.行番号は、0~(rowCount-1) の範囲
123         *   2.値配列は、not null、 かつ 1件以上
124         *   3.値配列の個数は、内部カラム数と同じ
125         *
126         * ここで登録した値は、内部の値配列と別管理されますので、セット後に、再びゲットしても
127         * ここでセットした値を取り出すことはできません。
128         * また、同じ行番号でセットした場合は、後でセットした値が有効です。
129         *
130         * ※ インデックス(row)とは、このArrayTableModel に持つ vals 配列の行のインデックスです。
131         * よって、オリジナルのDBTableModelの行番号ではありません。
132         *
133         * @og.rev 5.6.0.3 (2012/01/24) 変更された値を、書き戻す機能を追加します。
134         *
135         * @param   vals  配列値
136         * @param   row   追加するインデックス
137         * @throws      IllegalArgumentException 引数が1,2,3の条件を満たさない場合。
138         */
139        public void setValues( final String[] vals, final int row ) {
140                if( row < 0 || row > getRowCount() ) {
141                        String errMsg = "引数のインデックスは、0~" + (getRowCount()-1) + " の間で指定してください。index=[" + row + "]";
142                        throw new IllegalArgumentException( errMsg );
143                }
144                else if( vals == null || vals.length == 0 ) {
145                        String errMsg = "引数の値配列に、null、または 0件配列は指定できません。index=[" + row + "]";
146                        throw new IllegalArgumentException( errMsg );
147                }
148                else if( vals.length != names.length ) {
149                        String errMsg = "引数の値配列の個数と、内部カラム数が一致しません。"
150                                                + " index=[" + row + "] : 引数個数=[" + vals.length + "] != 内部カラム数=[" + names.length + "]";
151                        throw new IllegalArgumentException( errMsg );
152                }
153
154                if( rtnMap == null ) { rtnMap = new HashMap<Integer,String[]>(); }
155
156                int cols = vals.length;
157                String[] newVals = new String[cols];
158                System.arraycopy( vals, 0, newVals, 0, cols );
159
160                rtnMap.put( Integer.valueOf( row ) , newVals );
161        }
162
163        /**
164         * BizLogicで、データが変更された場合は、このMapで値の配列を返します。
165         * Mapのキーは、インデックス(row)のIntegerオブジェクトです。値は、設定された String配列です。
166         * なにも変更がされていなければ、null を返します。
167         *
168         * ※ インデックス(row)とは、このArrayTableModel に持つ vals 配列の行のインデックスです。
169         * よって、オリジナルのDBTableModelの行番号ではありません。
170         *
171         * @og.rev 5.6.0.3 (2012/01/24) 変更された値を、書き戻すためのMap&lt;インデックス,値配列&gt; を返します。
172         *
173         * @return      書き戻すためのMap<インデックス,値配列>
174         * @see AbstractBizLogic#isRequireTable()
175         */
176        public Map<Integer,String[]> getModifyVals() {
177                return rtnMap;
178        }
179
180        /**
181         * カラム名に対応する カラム番号を返します。
182         *
183         * 特殊なカラムが指定された場合は、負の値を返します。
184         * 例えば、[KEY.カラム名]、[I]、[ROW.ID] など、特定の負の値を返します。
185         * また、カラム名が元のデータモデルに存在しない場合も、負の値か、
186         * Exception を返します。負の値なのか、Exception なのかは、
187         * 実装に依存します。
188         *
189         * @param       columnName      値が参照されるカラム名
190         *
191         * @return  指定されたセルのカラム番号。存在しなければ、-1
192         * @throws  IllegalArgumentException 引数のカラム名が null の場合
193         */
194        public int getColumnNo( final String columnName ) {
195                if( columnName == null ) {
196                        String errMsg = "引数のカラム名に、null は設定できません。";
197                        throw new IllegalArgumentException( errMsg );
198                }
199
200                int address = -1;
201                for( int i = 0; i < names.length; i++ ) {
202                        if( columnName.equalsIgnoreCase( names[i] ) ) {
203                                address = i;
204                                break;
205                        }
206                }
207
208                return address;
209        }
210
211        /**
212         * カラム名配列に対応する カラム番号配列を返します。
213         *
214         * これは、#getColumnNo( String ) に対する 複数のカラム名を検索した
215         * 場合と同じです。
216         *
217         * @param       clmNms  値が参照されるカラム名配列
218         *
219         * @return  指定されたセルのカラム番号配列。
220         */
221        public int[] getColumnNos( final String[] clmNms ) {
222                if( clmNms == null ) { return new int[0]; }
223
224                int[] clmNos = new int[clmNms.length];
225                for( int j = 0; j < clmNms.length; j++ ) {
226                        int address = -1;
227                        for( int i = 0; i < names.length; i++ ) {
228                                if( clmNms[j].equalsIgnoreCase( names[i] ) ) {
229                                        address = i;
230                                        break;
231                                }
232                        }
233                        clmNos[j] = address;
234                }
235
236                return clmNos;
237        }
238
239        /**
240         * カラム名配列を返します。
241         *
242         * @return      カラム名配列
243         */
244        public String[] getNames() {
245                return names.clone();
246        }
247
248        /**
249         * row にあるセルの属性値を配列で返します。
250         *
251         * @param   row     値が参照される行
252         *
253         * @return  指定されたセルの属性値配列
254         */
255        public String[] getValues( final int row ) {
256                return vals[row].clone();
257        }
258
259        /**
260         * row および clm にあるセルの属性値をStringに変換して返します。
261         *
262         * @param   row     値が参照される行
263         * @param   clm     値が参照される列
264         *
265         * @return  指定されたセルの値
266         *
267         */
268        public String getValue( final int row, final int clm ) {
269                return vals[row][clm];
270        }
271
272        /**
273         * row および clm にあるセルの属性値をStringに変換して返します。
274         *
275         * @param   row     値が参照される行
276         * @param   clm     値が参照される列(キー)
277         *
278         * @return  指定されたセルの値
279         *
280         */
281        public String getValue( final int row, final String clm ) {
282                return vals[row][getColumnNo( clm )];
283        }
284
285        /**
286         * データテーブル内の行の数を返します。
287         *
288         * @return  モデルの行数
289         *
290         */
291        public int getRowCount() {
292                return vals.length;
293        }
294
295        /**
296         * row 単位に変更されたタイプ(追加/変更/削除)を返します。
297         * タイプは始めに一度登録するとそれ以降に変更はかかりません。
298         * つまり、始めに 追加で作成したデータは、その後変更があっても追加のままです。
299         * なにも変更されていない場合は, ""(ゼロストリング)を返します。
300         *
301         * @param   row     値が参照される行
302         *
303         * @return  変更されたタイプの値
304         */
305        public String getModifyType( final int row ) {
306                return modTypes == null ? "" : modTypes[row];
307        }
308
309        /**
310         * clm のNativeタイプを返します。
311         * Nativeタイプはorg.opengion.fukurou.model.NativeTypeで定義されています。
312         *
313         * @og.rev 5.1.8.0 (2010/07/01) NativeType#getType(String) のメソッドを使用するように変更。
314         *
315         * @param  clm      値が参照される列
316         *
317         * @return Nativeタイプ
318         * @see org.opengion.fukurou.model.NativeType
319         */
320        public NativeType getNativeType( final int clm ) {
321                return NativeType.getType( vals[0][clm] );
322        }
323
324        /**
325         * このオブジェクトの文字列表記を返します。
326         * デバッグ用です。
327         *
328         * @og.rev 5.6.7.0 (2013/07/27) 新規追加
329         *
330         * @return 文字列表現
331         * @see java.lang.Object#toString()
332         */
333        @Override
334        public String toString() {
335                StringBuilder buf = new StringBuilder();
336
337                buf.append( "NAMES=" ).append( Arrays.toString( names ) ).append( CR )
338                        .append( " COL_LEN=" ).append( (names != null) ? names.length : -1 ).append( CR )
339                        .append( " ROW_LEN=" ).append( (vals  != null) ? vals.length  : -1 ).append( CR ) ;
340
341                return buf.toString();
342        }
343}