    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.hayabusa.html;
    017
    018import org.opengion.hayabusa.common.HybsSystem;
    019import org.opengion.hayabusa.common.HybsSystemException;
    020import org.opengion.hayabusa.db.DBTableModel;
    021import org.opengion.fukurou.util.StringUtil;
    022import org.opengion.fukurou.model.Formatter;
    023
    024import java.util.regex.Pattern;
    025import java.util.regex.Matcher;
    026
    027/**
    028 * [PN],[OYA] などの [] で指定されたカラムで表されたフォーマットデータに対して、
    029 * DBTableModelオブジェクトを適用して 各カラムに実データを割り当てるオブジェクトです。
    030 *
    031 * 特に、[XXXX]に対して、[#XXXX]、[$XXXX]、[$XXXX]などの特殊記号が使用できます。
    032 * 特殊記号の解釈は、HTMLFormatTextField系とHTMLFormatTable系で異なりますので
    033 * ご注意ください。
    034 *
    035 * @og.rev 3.5.4.0 (2003/11/25) 新規追加
    036 * @og.group 画面表示
    037 *
    038 * @version  4.0
    039 * @author   Kazuhiko Hasegawa
    040 * @since    JDK5.0,
    041 */
    042public class TableFormatter {
    043
    044        /** フォーマットタイプの指定の特殊なマーク {@value} */
    045        public static final String HYBS_ITD_MARKER = "h_itd_marker";
    046        private static final Pattern ptnKey = Pattern.compile( "[ \t]+</td" ); // 4.3.2.0 (2008/09/10)
    047
    048        private FormatterType   formatType      = null;
    049        private int[]                   location        = null;
    050        private String[]                format          = null;
    051        private String                  formatTag       = null;
    052        private String                  rowspan         = " rowspan=\"2\"";
    053        private String                  trTag           = null;
    054        private boolean                 noClass         = false;
    055        // 3.5.6.0 (2004/06/18) '!' 値のみ 追加 既存の '$' は、レンデラー
    056        private char[]                  type            = null;         // '#':ラベルのみ  '$':レンデラー '!':値のみ  その他:通常
    057        private String                  usableKey       = null;         // キー情報のカラム文字列
    058        private int                             usableKeyNo     = -1;           // キー情報のカラム番号
    059        private String                  usableList      = "1" ;
    060
    061        private String                  keyBreakClm     = null;         // 5.7.6.3 (2014/05/23) キーブレイクをチェックするカラムID
    062        private int                             breakClmNo      = -1;           // 5.7.6.3 (2014/05/23) キーブレイクカラム番号
    063        private String                  breakVal        = null;         // 5.7.6.3 (2014/05/23) キーブレイクをチェックする値
    064
    065        private String                  itdBody         = "";           // 3.5.6.0 (2004/06/18) 追加
    066        private Formatter               formatter       = null;
    067
    068        /**
    069         * フォーマットをセットします。
    070         * フォーマットに、&lt;table&gt;を含む場合、TextField扱いなので、フォーマット分割
    071         * しません。table を含まず、tr を含む場合は、１行分のデータとして扱う為、
    072         * trTag を求めます。
    073         * trTag と format との間に、行ヘッダーが入ります。
    074         * Tomcat6では、JSPのパース時に、tabやspaceはそのままパースされるため、&lt;/td&gt;前
    075         * のスペース削除処理も行います。
    076         *
    077         * @og.rev 4.3.2.0 (2008/09/10) &lt;/td&gt;前のスペースを取り消します。
    078         * @og.rev 5.5.0.3 (2012/03/13) &lt;tr&gt;を取らないフラグ追加
    079         *
    080         * @param       fmt  [カラム名] 形式のフォーマットデータ
    081         * @param   flg  falseにすると先頭のtrタグを取る処理を行いません(5.5.0.3)
    082         */
    083        public void setFormat( final String fmt , final boolean flg ) {
    084                int tbl = fmt.indexOf( "<table" );
    085                int str = fmt.indexOf( "<tr" );
    086
    087                // tr を含み、かつ、tableを含まないか、含んでも tr の後ろにtableがある場合。
    088                if( str >= 0 && ( tbl < 0 || str < tbl ) && flg ) { // 5.5.0.3(2012/03/13)
    089                        int end = fmt.indexOf( '>',str );
    090                        formatTag = fmt.substring(end+1);
    091                        trTag = fmt.substring(0,end+1) ;
    092                }
    093                else {
    094                        formatTag = fmt;
    095                        trTag     = null;
    096                }
    097                // 4.3.2.0 (2008/09/10) </td>前のスペースを取り消す。
    098                Matcher matcher = ptnKey.matcher( formatTag );
    099                formatTag = matcher.replaceAll( "</td" );
    100
    101        }
    102
    103        /**
    104         * フォーマットをセットします。
    105         * フォーマットに、&lt;table&gt;を含む場合、TextField扱いなので、フォーマット分割
    106         * しません。table を含まず、tr を含む場合は、１行分のデータとして扱う為、
    107         * trTag を求めます。
    108         * trTag と format との間に、行ヘッダーが入ります。
    109         * Tomcat6では、JSPのパース時に、tabやspaceはそのままパースされるため、&lt;/td&gt;前
    110         * のスペース削除処理も行います。
    111         *
    112         * @og.rev 5.5.0.3 (2012/03/13) 引数追加につき。
    113         *
    114         * @param       fmt  [カラム名] 形式のフォーマットデータ
    115         */
    116        public void setFormat( final String fmt  ) {
    117                setFormat( fmt , true );
    118        }
    119
    120        /**
    121         * フォーマットを取得します。
    122         *
    123         * @og.rev 3.5.5.8 (2004/05/20) 新規追加
    124         * @og.rev 5.1.7.0 (2010/06/01) サニタイズ戻し処理("\\]\\"から"["に戻し)を追加
    125         *
    126         * @return      フォーマットデータ
    127         */
    128        public String getFormat() {
    129                if( trTag != null ) {
    130                        return decodeSanitizedStr( trTag + formatTag );
    131                }
    132                else {
    133                        return decodeSanitizedStr( formatTag );
    134                }
    135        }
    136
    137        /**
    138         * DBTableModelを利用して、フォーマットデータを初期化します。
    139         *
    140         * @og.rev 3.5.5.0 (2004/03/12) [KEY.カラム名] 機能追加
    141         * @og.rev 3.5.5.2 (2004/04/02) [I] で、行番号を作成します。
    142         * @og.rev 3.5.6.0 (2004/06/18) '!' 値のみ 追加 既存の '$' は、レンデラー
    143         * @og.rev 3.6.0.0 (2004/09/17) [ROW.ID] で、行毎のチェックボックスのIDを返します。
    144         * @og.rev 5.1.7.0 (2010/06/01) サニタイズ戻し処理("\\]\\"から"["に戻し)を追加
    145         * @og.rev 5.7.6.3 (2014/05/23) キーブレイクをチェックする keyBreakClm 属性追加
    146         *
    147         * @param       table   DBTableModelオブジェクト
    148         */
    149        public void makeFormat( final DBTableModel table ) {
    150                formatter = new Formatter( table );
    151                formatter.setFormat( formatTag );
    152                location = formatter.getClmNos();
    153                format   = formatter.getFormat();
    154
    155                // 5.1.7.0 (2010/06/01) サニタイズ戻し処理("\\]\\"から"["に戻し)を追加
    156                if( format != null ) {
    157                        for( int i=0; i<format.length; i++ ) {
    158                                format[i] = decodeSanitizedStr( format[i] );
    159                        }
    160                }
    161
    162                type     = formatter.getType();
    163
    164                // このフォーマットを使用するかどうかを指定する判定条件の初期設定です。
    165                if( usableKey != null ) {
    166                        usableKeyNo = table.getColumnNo( usableKey );
    167                }
    168
    169                // 5.7.6.3 (2014/05/23) キーブレイクをチェックする keyBreakClm 属性追加
    170                if( keyBreakClm != null ) {
    171                        breakClmNo = table.getColumnNo( keyBreakClm );
    172                        breakVal   = null;              // 初期化します。
    173                }
    174        }
    175
    176        /**
    177         * テーブルフォーマットのタイプを指定します。
    178         * enum FormatterType で、指定します。
    179         *
    180         * @og.rev 4.0.0.0 (2007/05/02) enum 定義に変更
    181         *
    182         * @param  ftype フォーマットのタイプ
    183         */
    184        public void setFormatType( final FormatterType ftype ) {
    185                formatType = ftype;
    186        }
    187
    188        /**
    189         * このフォーマットのタイプを返します。
    190         *
    191         * このフォーマットのタイプを返します。
    192         *
    193         * @og.rev 4.0.0.0 (2007/05/02) enum 定義に変更
    194         *
    195         * @return      このフォーマットのタイプを返します。
    196         */
    197        public FormatterType getFormatType() {
    198                return formatType;
    199        }
    200
    201        /**
    202         * テーブルの rowspan 属性をセットします。
    203         * rowspan は、ヘッダー部のフォーマットの行数です。初期値は ２行 です。
    204         * 設定は、"2" などの、数字部のみをセットします。
    205         *
    206         * @param  rowspan 属性
    207         */
    208        public void setRowspan( final String rowspan ) {
    209                if( rowspan == null || rowspan.length() == 0 || rowspan.equals( "1" ) ) {
    210                        this.rowspan = "";
    211                }
    212                else {
    213                        this.rowspan = " rowspan=\"" + rowspan + "\"";
    214                }
    215        }
    216
    217        /**
    218         * 設定された rowspan を返します。
    219         * これは、フォーマットの段組の数を取り出します。
    220         * 文字列としては、rowspan="2" という形で取り出します。
    221         *
    222         * @return フォーマット文字列
    223         */
    224        public String getRowspan() {
    225                return rowspan;
    226        }
    227
    228        /**
    229         * ロケーション番号のサイズを返します。
    230         * フォーム位置番号は、0 から getLocationSize()-1 までの数字を指定します。
    231         * ロケーションサイズは、aaa[ABC]bbb[DEF]ccc[GHI]ddd となっている場合、
    232         * aaa , bbb , ccc , ddd は、フォーマットで、サイズは４。
    233         * ABC , DEF , GHI に対応するカラム番号がロケーションで、サイズは３。
    234         * このメソッドで返すのは、ロケーション番号(３)の方です。
    235         *
    236         * @return  ロケーション番号のサイズ
    237         */
    238        public int getLocationSize() {
    239                return location.length;
    240        }
    241
    242        /**
    243         * カラムのロケーション番号を返します。
    244         * 引数は、0 から、getLocationSize()-1 までの数で指定します。
    245         * 指定の位置の、フォーマットのカラム名に対応するロケーション番号
    246         * を返します。
    247         *
    248         * @param no フォーム位置番号
    249         *
    250         * @return ロケーション番号
    251         */
    252        public int getLocation( final int no ) {
    253                return location[no];
    254        }
    255
    256        /**
    257         * フォーマット文字列を返します。
    258         * 引数は、0 から、getLocationSize() までの数で指定します。
    259         * 指定のフォーマットが、aaa[ABC]bbb[DEF]ccc[GHI]ddd となっている場合、
    260         * aaa , bbb , ccc , ddd を引数 0 , 1 , 2 , 3 で返します。
    261         *
    262         * @param no フォーム位置番号
    263         *
    264         * @return フォーマット文字列
    265         */
    266        public String getFormat( final int no ) {
    267                return format[no];
    268        }
    269
    270        /**
    271         * システムフォーマット文字列を返します。
    272         * システムフォーマット文字列は、[KEY.カラム名] などの特殊記号で指定された
    273         * カラム名の事で、location には、マイナスの値が設定されます。
    274         * マイナスの値に応じて、処理を変えることが出来ます。
    275         *
    276         * [KEY.カラム名] : 行番号付きカラム名
    277         * [I]            : 行番号
    278         * [ROW.ID]       : 行毎のチェックボックスのID
    279         * [ROW.JSON]     : 行毎の全データのJavaScriptオブジェクト形式
    280         *
    281         * @og.rev 3.5.5.0 (2004/03/12) [KEY.カラム名] 機能追加
    282         * @og.rev 3.5.5.2 (2004/04/02) [I] で、行番号を作成します。
    283         * @og.rev 3.6.0.0 (2004/09/17) [ROW.ID] で、行毎のチェックボックスのIDを返します。
    284         * @og.rev 4.0.0.0 (2007/05/02) Formatter を使用するように変更
    285         *
    286         * @param       row     行番号
    287         * @param       loc     位置番号
    288         *
    289         * @return フォーマット文字列
    290         */
    291        public String getSystemFormat( final int row,final int loc ) {
    292                if( loc == Formatter.SYS_ROWNUM ) {
    293                        return String.valueOf( row );
    294                }
    295                else if( loc == Formatter.SYS_JSON ) {
    296                        return formatter.getJson( row );
    297                }
    298
    299                String errMsg = "システムフォーマットは、下記の形式しか使用できません。[" + loc + "]" + HybsSystem.CR
    300                                + "  : [KEY.カラム名] : 行番号付きカラム名" + HybsSystem.CR
    301                                + "  : [I]            : 行番号" + HybsSystem.CR
    302                                + "  : [ROW.ID]       : 行毎のチェックボックスのID" + HybsSystem.CR
    303                                + "  : [ROW.JSON]     : 行毎の全データのJavaScriptオブジェクト形式" ;
    304                throw new HybsSystemException( errMsg );
    305        }
    306
    307        /**
    308         * タイプ文字列を返します。
    309         * タイプとは、[XXX] の記述で、[#XXX] は、XXXカラムのラベルを、[$XXX]は、XXXカラムの
    310         * レンデラーを、[!XXX} は、値のみ取り出す指定を行います。
    311         * 主に、TextField系のフォーマットとTable系では、意味合いが異なりますので、
    312         * ご注意ください。
    313         *
    314         * @param no フォーム位置番号
    315         *
    316         * @return タイプ文字列 '#':ラベルのみ  '$':レンデラー '!':値のみ  その他:通常
    317         */
    318        public char getType( final int no ) {
    319                return type[no];
    320        }
    321
    322        /**
    323         * 設定された フォーマットの trタグを返します。
    324         * これは、trタグにclass属性他の設定がされていた場合に、変換後の
    325         * 文字列にも反映させる為に必要です。
    326         *
    327         * @og.rev 5.1.7.0 (2010/06/01) サニタイズ戻し処理("\\]\\"から"["に戻し)を追加
    328         *
    329         * @return フォーマットの trタグ
    330         */
    331        public String getTrTag() {
    332                if( trTag == null ) { return ""; }
    333
    334                return decodeSanitizedStr( trTag ) ;
    335        }
    336
    337        /**
    338         * カラムのクラス名(X,S9 など)のセットを行うかどうか指定します。
    339         *
    340         * "true" で、クラス属性を設定しません。これは、ＣＳＳファイルに書かれている属性を
    341         * 使用しないことを意味します。
    342         * 初期値は、"false" です。
    343         *
    344         * @param       flag クラス名使用の有無(true:使用しない/false:使用する。)
    345         */
    346        public void setNoClass( final String flag ) {
    347                noClass = StringUtil.nval( flag,noClass );
    348        }
    349
    350        /**
    351         * カラムのクラス名(X,S9 など)のセットを行うかどうか取得します。
    352         *
    353         * "true" で、クラス属性を設定しません。これは、ＣＳＳファイルに書かれている属性を
    354         * 使用しないことを意味します。
    355         * 初期値は、"false" です。
    356         *
    357         * @return      クラス名使用の有無(true:使用しない/false:使用する。)
    358         */
    359        public boolean isNoClass() {
    360                return noClass;
    361        }
    362
    363        /**
    364         * フォーマットの使用可否を判断するキーとなるカラム名を指定します。
    365         *
    366         * キーが、usableList に含まれる場合は、このフォームを使用できます。
    367         * キー(カラム名)が指定されない場合は、常に使用されます。
    368         * ※ 現時点では、BODYタイプのみ使用しています。
    369         *
    370         * @param  key フォーマットの使用可否を判断するカラム名
    371         */
    372        public void setUsableKey( final String key ) {
    373                usableKey = key;
    374        }
    375
    376        /**
    377         *  フォーマットの使用可否を判断する文字列リストを指定します。
    378         *
    379         * キーが、この文字列リスト中に存在する場合は、このフォームを使用できます。
    380         * この文字列リストは、固定な文字列です。{&#064;XXXX}は使用できますが、[XXXX]は
    381         * 使用できません。
    382         * 初期値は、"1" です。
    383         * ※ 現時点では、BODYタイプのみ使用しています。
    384         *
    385         * @param  list フォーマットの使用可否を判断する文字列リスト
    386         * @see TableFormatter#isUse( int,DBTableModel )
    387         */
    388        public void setUsableList( final String list ) {
    389                if( list != null ) {
    390                        usableList = list;
    391                }
    392        }
    393
    394        /**
    395         * ここで指定したカラムの値が、キーブレイクした場合、このタグを使用します。
    396         *
    397         * キーブレイクで 使用可否を指定する為の機能です。
    398         * この設定値は、usableKey,usableList とは、独立しているため、それぞれで
    399         * 有効になれば、使用されると判断されます。
    400         * キーブレイク判定では、最初の1件目は、必ず使用されると判断されます。
    401         *
    402         * @og.rev 5.7.6.3 (2014/05/23) 新規追加
    403         *
    404         * @param  kclm  キーブレイクをチェックするカラムID
    405         */
    406        public void setKeyBreakClm( final String kclm ) {
    407                keyBreakClm = kclm;
    408        }
    409
    410        /**
    411         * このフォーマットを使用するかどうかの問い合わせを返します。
    412         *
    413         * "true" で、使用します。setUsableKey( String ) で、指定された
    414         * カラム名の値が、setUsableList( String ) で指定された文字列に含まれていれば、
    415         * 使用します。カラム名がセットされない場合は、デフォルト値("true")が使用されます。
    416         * ※ 現時点では、BODYタイプのみ使用しています。
    417         * カラムのデータに、不正なスペースが入る場合を想定して、trim() しています。
    418         * よって、usableList の値にスペースは使用できません。
    419         *
    420         * 5.7.6.3 (2014/05/23) 以降は、keyBreakClm によるキーブレイクチェックも追加されました。
    421         * 従来の usableKey,usableList とは、独立しているため、それぞれで有効になれば、
    422         * 使用されると判断されます。
    423         *
    424         * @og.rev 3.5.6.2 (2004/07/05) 判定評価用カラムの値を trim() します。
    425         * @og.rev 5.7.6.3 (2014/05/23) キーブレイクをチェックする keyBreakClm 属性追加
    426         *
    427         * @param  row 行番号
    428         * @param       table   DBTableModelオブジェクト
    429         *
    430         * @return      このフォームを使用するかどうか(true:使用する/false:使用しない)
    431         * @see TableFormatter#setUsableKey( String )
    432         * @see TableFormatter#setUsableList( String )
    433         */
    434        public boolean isUse( final int row, final DBTableModel table ) {
    435// 5.7.6.3 (2014/05/23) keyBreakClm 属性の追加で、処理ロジックの見直し
    436//              if( usableKeyNo < 0 ) { return true; }
    437//              String val = table.getValue( row,usableKeyNo ).trim();
    438//              return val.length() == 0 ? false : usableList.indexOf( val ) >= 0 ;
    439
    440                // どちらも設定されていなければ、使用される(=true)
    441                if( usableKeyNo < 0 && breakClmNo < 0 ) { return true; }
    442
    443                // 以下、どちらかは設定されているため、true の時点で、使用される(=true)を返す。
    444                if( usableKeyNo >= 0 ) {
    445                        String val = table.getValue( row,usableKeyNo ).trim();
    446                        if( usableList.indexOf( val ) >= 0 ) { return true; }
    447                }
    448
    449                if( breakClmNo >= 0 ) {
    450                        String val = table.getValue( row,breakClmNo ).trim();
    451                        if( !val.equals( breakVal ) ) {                 // 同じでない場合は、true
    452                                breakVal = val;
    453                                return true;
    454                        }
    455                }
    456
    457                return false ;                  // 最後まで残ると、使用されないと判断、false を返す。
    458        }
    459
    460        /**
    461         *  itdフォーマット文字列を設定します。
    462         *
    463         * itd ボディ部の文字列を指定します。
    464         * itd ボディは、繰り返し処理を行います。これを、上位のボディ文字列の中の
    465         * HYBS_ITD_MARKER 文字列 と置き換えます。
    466         *
    467         * @og.rev 3.5.6.0 (2004/06/18) itdフォーマット文字列の取り込み
    468         *
    469         * @param  itd itdフォーマットの文字列
    470         */
    471        public void setItdBody( final String itd ) {
    472                if( itd != null ) {
    473                        itdBody = itd;
    474                }
    475        }
    476
    477        /**
    478         *  itdフォーマット文字列を取得します。
    479         *
    480         * itd ボディ部の文字列を取得します。
    481         * itd ボディは、繰り返し処理を行います。これを、上位のボディ文字列の中の
    482         * HYBS_ITD_MARKER 文字列 と置き換えます。
    483         *
    484         * @og.rev 3.5.6.0 (2004/06/18) itdフォーマット文字列の取り込み
    485         *
    486         * @return      itdフォーマットの文字列
    487         */
    488        public String getItdBody() {
    489                return itdBody;
    490        }
    491
    492        /**
    493         * サニタイズの戻し("\\]\\"から"["に戻し)を行います。
    494         *
    495         * @og.rev 5.1.7.0 (2010/06/01) 新規作成
    496         *
    497         * @param str サニタイズされた文字列
    498         *
    499         * @return サニタイズ戻し処理後の文字列
    500         */
    501        private String decodeSanitizedStr( final String str ) {
    502                if( str != null && str.indexOf( "\\]\\" ) >= 0 ) {
    503                        return str.replace( "\\]\\", "[" );
    504                }
    505                else {
    506                        return str;
    507                }
    508        }
    509}