    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}