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.model;
017
018import java.util.ArrayList;
019import java.util.List;
020
021
022/**
023 * [PN],[OYA] などの [] で指定されたカラムで表されたフォーマットデータに対して、
024 * DataModel オブジェクトを適用して 各カラムに実データを割り当てるオブジェクトです。
025 *
026 * カラム名には、特殊カラム名が使用できます。これは、DataModel に存在しないカラム名
027 * ですが、値を返すことが出来ます。
028 * <pre>
029 * [KEY.カラム名] : 行番号付きカラム名
030 * [I]            : 行番号
031 * [ROW.ID]       : 行毎のチェックボックスのID
032 * [ROW.JSON]     : 行毎の全データのJavaScriptオブジェクト形式 { key:val,key:val,... }
033 * カラムの前に修飾記号(#,$,!)を付けるとフォーマットを変更できます。
034 * ただし、FormatTextField 系 と FormatTable 系で、出力される形式が異なります。
035 *                  FormatTextField 系               FormatTable 系
036 * [#カラム名]    : TDなしのラベルと入力フィールド   ラベルを出力
037 * [$カラム名]    : TDなしの入力フィールドのみ       レンデラーを出力
038 * [!カラム名]    : TDなしの値のみ                   値を出力
039 *
040 * </pre>
041 * @og.group 画面表示
042 *
043 * @version  4.0
044 * @author   Kazuhiko Hasegawa
045 * @since    JDK5.0,
046 */
047public class Formatter {
048        /** カラムID(連結文字列)行番号の連結文字列を定義 {@value} */
049        public static final String JOINT_STRING = "__" ;
050
051        /** テーブル表示のチェックボックスを特定する id の 名称( id は、この名称+行番号) {@value} */
052        public static final String ROW_ID_KEY = "cb";   // 3.6.0.0 (2004/09/17)
053
054        /** 特殊カラム名の定義: 行番号 [I]  */
055        public static final int SYS_ROWNUM      = -1;           // [KEY.カラム名],[I],[ROW.ID]
056        /** 特殊カラム名の定義: [ROW.JSON]       */
057        public static final int SYS_JSON        = -2;           // [ROW.JSON]
058
059        private final DataModel<?>        model   ;                       // 4.3.3.6 (2008/11/15) Generics警告対応
060        private int[]                           clmNos  = null;         // フォーマットのカラム番号配列
061        private String[]                        format  = null;
062        private String[]                        clmKeys = null;         // フォーマットのカラム名配列
063        private char[]                          type    = null;         // '#':ラベルのみ  '$':レンデラー '!':値のみ  その他:通常
064
065        /**
066         * データモデルを指定してフォーマッターを構築します。
067         *
068         * @param  model データモデル
069         */
070        public Formatter( final DataModel<?> model ) {            // 4.3.3.6 (2008/11/15) Generics警告対応
071                this.model = model;
072        }
073
074        /**
075         * フォーマットをセットします。
076         *
077         * @param       fmt  [カラム名] 形式のフォーマットデータ
078         */
079        public void setFormat( final String fmt ) {
080                makeFormatList( fmt );
081                advanceFormat();
082        }
083
084        /**
085         * フォーマットをセットします。
086         *
087         * @param       fmt  [カラム名] 形式のフォーマットデータ
088         */
089        private void makeFormatList( final String fmt ) {
090                int start = 0;
091                int index = fmt.indexOf( '[' );
092                List<String> clmKeyList = new ArrayList<String>();
093                List<String> formatList = new ArrayList<String>();
094                while( index >= 0 ) {
095                        int end = fmt.indexOf( ']',index );
096                        if( end < 0 ) {
097                                String errMsg = "[ と ] との対応関係がずれています。"
098                                                        + "format=[" + fmt + "] : index=" + index ;
099                                throw new RuntimeException( errMsg );
100                        }
101
102                        // [ より前方の文字列は、formatList へ
103                        if( index > 0 ) { formatList.add( fmt.substring( start,index ) ); }
104                        else                    { formatList.add( "" ); }       // ][ と連続しているケース
105
106                        // [XXXX] の XXXX部分を処理
107                        clmKeyList.add( fmt.substring( index+1,end ) );
108
109                        start = end+1 ;
110                        index = fmt.indexOf( '[',start );
111                }
112                // ] の後方部分は、formatList へ
113                formatList.add( fmt.substring( start ) );
114
115                format  = formatList.toArray( new String[formatList.size()] );
116                clmKeys = clmKeyList.toArray( new String[clmKeyList.size()] );
117        }
118
119        /**
120         * 追加機能フォーマットを作成します。
121         *
122         */
123        private void advanceFormat() {
124
125                int size = clmKeys.length ;
126                clmNos = new int[size];
127                type   = new char[size];
128
129                // カラム番号の設定と、特殊カラム名処理
130                String clm ;
131                for( int i=0; i<size; i++ ) {
132                        clm = clmKeys[i];
133                        char ch = clm.charAt(0);
134                        if( ch == '#' || ch == '$' || ch == '!' ) {
135                                type[i] = ch;
136                                clm = clm.substring(1);
137                                clmKeys[i]      = clm;
138                                clmNos[i] = model.getColumnNo( clm );
139                        }
140                        // [KEY.カラム名] 機能追加
141                        else if( clm.startsWith( "KEY." ) ) {
142                                clmNos[i]       = SYS_ROWNUM;
143                                format[i]       = format[i] + clm.substring(4) + JOINT_STRING ;
144                        }
145                        // [I] 機能追加
146                        else if( "I".equals( clm ) ) {
147                                clmNos[i]       = SYS_ROWNUM;
148                        }
149                        // [ROW.ID] 機能追加
150                        else if( "ROW.ID".equals( clm ) ) {
151                                clmNos[i] = SYS_ROWNUM;
152                                format[i] = format[i] + ROW_ID_KEY ;
153                        }
154                        // [ROW.JSON] 機能追加
155                        else if( "ROW.JSON".equals( clm ) ) {
156                                clmNos[i] = SYS_JSON;
157                        }
158                        else {
159                                clmNos[i] = model.getColumnNo( clm );
160                        }
161                }
162        }
163
164        /**
165         * column にあるセルの属性値をStringに変換して返します。
166         *
167         * @param       row     処理中の行番号
168         * @param       clm     値が参照されるカラム番号
169         *
170         * @return      指定されたセルの値
171         *
172         */
173        public String getValue( final int row,final int clm ) {
174                final String rtn ;
175                if( clm >= 0 ) {
176                        rtn = String.valueOf( model.getValue( row,clm ) );
177                }
178                else if( clm == SYS_ROWNUM ) {
179                        rtn = String.valueOf( row );
180                }
181                else if( clm == SYS_JSON ) {
182                        rtn = getJson( row );
183                }
184                else {
185                        String errMsg = "指定のカラム番号に該当する処理が見つかりません。"
186                                                + "clm=[" + clm + "]" ;
187                        throw new RuntimeException( errMsg );
188                }
189
190                return rtn ;
191        }
192
193        /**
194         * 指定の 行番号に対する、DataModel を元に作成したフォーマット文字列を返します。
195         *
196         * @param       row     行番号( [I]フォーマット処理用 )
197         *
198         * @return  指定のObject配列を元に作成したフォーマット文字列
199         */
200        public String getFormatString( final int row ) {
201//              StringBuilder rtnStr = new StringBuilder( 200 );
202//
203//              int count = clmNos.length;
204//              for( int i=0; i<count; i++ ) {
205//                      rtnStr.append( format[i] );
206//                      if( clmNos[i] == SYS_ROWNUM ) {
207//                              rtnStr.append( row );
208//                      }
209//                      else if( clmNos[i] == SYS_JSON ) {
210//                              rtnStr.append( getJson( row ) );
211//                      }
212//                      else {
213//                              rtnStr.append( model.getValue( row,clmNos[i] ) );
214//                      }
215//              }
216//              rtnStr.append( format[count] );
217//
218//              return rtnStr.toString();
219                return getFormatString( row, null );
220        }
221
222        /**
223         * 指定の 行番号に対する、DataModel を元に作成したフォーマット文字列を返します。
224         * データはseparatorで指定された区切り文字で囲まれて返されます。
225         *
226         * @og.rev 4.3.1.1 (2008/08/23) switch に、default label が存在しないため、追加
227         *
228         * @param       row     行番号( [I]フォーマット処理用 )
229         * @param       separator       セパレーター
230         *
231         * @return  指定のObject配列を元に作成したフォーマット文字列
232         */
233        public String getFormatString( final int row, final String separator ) {
234                StringBuilder rtnStr = new StringBuilder( 200 );
235
236                int count = clmNos.length;
237                for( int i=0; i<count; i++ ) {
238                        rtnStr.append( format[i] );
239                        if( clmNos[i] == SYS_ROWNUM ) {
240                                rtnStr.append( row );
241                        }
242                        else if( clmNos[i] == SYS_JSON ) {
243                                rtnStr.append( getJson( row ) );
244                        }
245                        else {
246                                if( separator == null || separator.length() == 0 ) {
247                                        rtnStr.append( model.getValue( row,clmNos[i] ) );
248                                }
249                                else {
250                                        // 4.3.1.1 (2008/08/23) default label が存在しないため、追加
251                                        switch( model.getNativeType( clmNos[i] ) ) {
252                                                case INT:
253                                                case LONG:
254                                                case DOUBLE:
255                                                        rtnStr.append( model.getValue( row,clmNos[i] ) );
256                                                        break;
257                                                case STRING:
258                                                case CALENDAR:
259                                                        rtnStr.append( separator ).append( model.getValue( row,clmNos[i] ) ).append( separator );
260                                                        break;
261                                                default:
262                                                        throw new AssertionError( "Unexpected enumrated value! " + model.getNativeType( clmNos[i] ) );
263                                        }
264                                }
265                        }
266                }
267                rtnStr.append( format[count] );
268
269                return rtnStr.toString();
270        }
271
272        /**
273         * 先のフォーマット情報の[カラム名]を、? に置き換えたフォーマットを返します。
274         * これは、java.sql.PreparedStatement 形式のQuery文字列を合成する場合に使用します。
275         *
276         * @return  PreparedStatement形式のQuery文字列
277         */
278        public String getQueryFormatString() {
279                StringBuilder rtnStr = new StringBuilder( 200 );
280
281                int count = clmKeys.length;
282                for( int i=0; i<count; i++ ) {
283                        rtnStr.append( format[i] ).append( '?' );
284                }
285                rtnStr.append( format[count] );
286
287                return rtnStr.toString();
288        }
289
290        /**
291         * フォーマットのカラム名配列を返します。
292         *
293         * @return      フォーマットのカラム名配列
294         */
295        public String[] getClmKeys() {
296                return clmKeys.clone();
297        }
298
299        /**
300         * フォーマットのカラム番号配列を返します。
301         *
302         * @return      フォーマットのカラム番号配列
303         */
304        public int[] getClmNos() {
305                return clmNos.clone();
306        }
307
308        /**
309         * フォーマット配列を返します。
310         *
311         * @return      フォーマット配列
312         */
313        public String[] getFormat() {
314                return format.clone();
315        }
316
317        /**
318         * タイプ文字列配列を返します。
319         * タイプとは、[XXX] の記述で、[#XXX] は、XXXカラムのラベルを、[$XXX]は、XXXカラムの
320         * レンデラーを、[!XXX} は、値のみ取り出す指定を行います。
321         * 主に、TextField系のフォーマットとTable系では、意味合いが異なりますので、
322         * ご注意ください。
323         *
324         * @return タイプ文字列配列 '#':ラベルのみ  '$':レンデラー '!':値のみ  その他:通常
325         */
326        public char[] getType() {
327                return type.clone();
328        }
329
330        /**
331         * 行毎の全データのJavaScriptオブジェクト形式 を返します。
332         *
333         * JavaScriptオブジェクト形式とは、{ key:val,key:val,... }の形式で、
334         * ここでは、内部設定された DataModel のすべてのカラム名をキーに、
335         * 引数で渡された 配列を 値として使用します。
336         *
337         * @param       row     (DataModelの)行番号
338         *
339         * @return  指定の行番号に対応する全データのJSON形式データ
340         */
341        public String getJson( final int row ) {
342                String[] names = model.getNames();
343                Object[] vals  = model.getValues( row );
344
345                StringBuilder rtnStr = new StringBuilder( 200 );
346
347                rtnStr.append( "{'I':'" ).append( row ).append( "'" );  // 行番号
348
349                for( int i=0; i<names.length; i++ ) {
350                        rtnStr.append( ",'" ).append( names[i] ).append( "':'" );
351                        rtnStr.append( vals[i] ).append( "'" );
352                }
353                rtnStr.append( "}" );
354
355                return rtnStr.toString();
356        }
357}