    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.taglib;
    017
    018import static org.opengion.fukurou.util.StringUtil.*;
    019
    020import java.io.BufferedReader;
    021import java.io.File;
    022import java.io.IOException;
    023import java.io.InputStream;
    024import java.io.InputStreamReader;
    025import java.io.ObjectInputStream;
    026import java.io.ObjectOutputStream;
    027import java.io.UnsupportedEncodingException;
    028import java.nio.file.Files;
    029import java.util.Locale ;
    030
    031import javax.servlet.http.HttpServletRequest;
    032import javax.servlet.http.HttpSession;
    033
    034import org.opengion.fukurou.util.Closer ;
    035import org.opengion.fukurou.util.ErrorMessage;
    036import org.opengion.fukurou.util.FileUtil;
    037import org.opengion.fukurou.util.StringUtil ;
    038import org.opengion.hayabusa.common.HybsSystem;
    039import org.opengion.hayabusa.common.HybsSystemException;
    040import org.opengion.hayabusa.db.DBColumn;
    041import org.opengion.hayabusa.db.DBTableModel;
    042import org.opengion.hayabusa.io.StorageAPI;
    043import org.opengion.hayabusa.io.StorageAPIFactory;
    044import org.opengion.hayabusa.io.TableReader;
    045
    046/**
    047 * 指定のファイルを DBTableModelオブジェクトに読み取るファイル入力タグです。
    048 *
    049 * データ(DBTableModel)と、コントローラ(ReadTableタグ)を与えて、外部からコントロールすることで、
    050 * 各種形式で データ(DBTableModel)を表示させることが できます。
    051 * ReadTableタグ に対して、コマンドを与えることにより、内部のコントローラの実装に対応した
    052 * 形式でデータを作成します。
    053 * すべての読取の初期クラス名を リソースファイルの TABLE_READER_DEFAULT_CLASS で指定可能です。
    054 * その場合、AutoReader を指定すると、Excel と Default(テキスト) を順番に試します。
    055 *
    056 * 入力件数を"DB.COUNT" キーでリクエストにセットしています。
    057 *
    058 * @og.formSample
    059 * ●形式：
    060 *     &lt;og:readTable
    061 *         command      = "NEW"
    062 *         fileURL      = "{&#064;USER.ID}"     読み取り元ディレクトリ名
    063 *         filename     = "{&#064;filename}"    読み取り元ファイル名
    064 *         encode       = "UnicodeLittle"       読み取り元ファイルエンコード名
    065 *         maxRowCount  = "10000"               読取最大件数(0:[無制限])
    066 *     /&gt;
    067 * ●body：なし
    068 *
    069 * ●Tag定義：
    070 *   &lt;og:readTable
    071 *       readerClass        【TAG】実際に読み出すクラス名の略称(TableReader_**** の ****)をセットします({@og.doc03Link readerClass 初期値:Default})
    072 *       fileURL            【TAG】読み取り元ディレクトリ名を指定します(初期値:FILE_URL)
    073 *       filename           【TAG】ファイルを作成するときのファイル名をセットします (初期値:FILE_FILENAME[=file.xls])
    074 *       encode             【TAG】ファイルを作成するときのファイルエンコーディング名をセットします(初期値:FILE_ENCODE)
    075 *       maxRowCount        【TAG】読取時の最大取り込み件数をセットします (初期値:DB_MAX_ROW_COUNT[=1000])(0:[無制限])
    076 *       separator          【TAG】可変長ファイルを作成するときの項目区切り文字をセットします
    077 *       tableId            【TAG】(通常使いません)sessionから所得する DBTableModelオブジェクトの ID
    078 *       command            【TAG】コマンド(NEW,RENEW)をセットします(初期値:NEW)
    079 *       modifyType         【TAG】ファイル取り込み時の モディファイタイプ(A(追加),C(更新),D(削除))を指定します
    080 *       displayMsg         【TAG】query の結果を画面上に表示するメッセージIDを指定します(初期値:MSG0033[　件検索しました])
    081 *       notfoundMsg        【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])
    082 *       sheetName          【TAG】EXCELファイルを読み込むときのシート名を設定します(初期値:指定なし)
    083 *       sheetNos           【TAG】EXCELファイルを読み込むときのシート番号を複数設定できます(初期値:0)
    084 *       sheetConstKeys     【TAG】EXCELファイルを読み込むときの固定値となるカラム名(CSV形式)
    085 *       sheetConstAdrs     【TAG】EXCELファイルを読み込むときの固定値となるアドレス(行-列,行-列,・・・)
    086 *       nullBreakClm       【TAG】カラム列に NULL が現れた時点で読み取りを中止します(複数Sheetの場合は、次のSheetを読みます)。
    087 *       columns            【TAG】読み取り元ファイルのカラム列を、外部(タグ)より指定します
    088 *       useNumber          【TAG】行番号情報を、使用している/していない[true/false]を指定します(初期値:true)
    089 *       adjustColumns      【TAG】読み取り元ファイルのデータ変換を行うカラム列をカンマ指定します
    090 *       checkColumns       【TAG】読み取り元ファイルの整合性チェックを行うカラム列をカンマ指定します
    091 *       nullCheck          【TAG】NULL チェックすべきカラム列をカンマ区切り(CVS形式)で指定します
    092 *       language           【TAG】タグ内部で使用する言語コード[ja/en/zh/…]を指定します
    093 *       stopZero           【TAG】読込件数が０件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する])
    094 *       scope              【TAG】キャッシュする場合のスコープ[request/page/session/applicaton]を指定します(初期値:session)
    095 *       mainTrans          【TAG】(通常使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:false)
    096 *       skipRowCount       【TAG】(通常は使いません)データの読み飛ばし件数を設定します
    097 *       useRenderer        【TAG】読取処理でラベルをコードリソースに逆変換を行うかどうかを指定します (初期値:USE_TABLE_READER_RENDERER[=false])
    098 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 5.7.7.2 (2014/06/20)
    099 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 5.7.7.2 (2014/06/20)
    100 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:true) 5.7.7.2 (2014/06/20)
    101 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:true) 5.7.7.2 (2014/06/20)
    102 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
    103 *   /&gt;
    104 *
    105 * ●使用例
    106 *
    107 *     &lt;og:readTable
    108 *         command        = "NEW"
    109 *         readerClass    = "Fixed"               固定長データの読み取り
    110 *         modifyType     = "{&#064;modifyType}"  読取時のモディファイタイプ(A,C等)
    111 *         fileURL        = "{&#064;USER.ID}"     読み取り元ディレクトリ名
    112 *         filename       = "{&#064;filename}"    読み取り元ファイル名
    113 *         encode         = "Shift_JIS"           読み取り元ファイルエンコード名
    114 *         maxRowCount    = "10000"               読取最大件数(0:[無制限])
    115 *         columns        = "OYA,KO,HJO,SU,DYSTR,DYEND"   #NAME に対応するカラム列
    116 *         useNumber      = "false"               行番号の存在しないデータを読み取ります。
    117 *         adjustColumns  = "OYA,KO,HJO,SU"       データ変換するカラム列("*" で全カラム)
    118 *         checkColumns   = "OYA,KO,HJO,SU"       整合性チェックするカラム列("*" で全カラム)
    119 *         nullCheck      = "OYA,KO,SU"           NULLチェックを実行します("*" で全カラム)
    120 *         stopZero       = "true"                取得0件の場合に以降の処理を停止します
    121 *         skipRowCount   = "4"                   データの読み飛ばし件数(読み込み開始は、この数字＋１行目から)
    122 *     /&gt;
    123 *
    124 * @og.group ファイル入力
    125 *
    126 * @version  4.0
    127 * @author   Kazuhiko Hasegawa
    128 * @since    JDK5.0,
    129 */
    130public class ReadTableTag extends CommonTagSupport {
    131        //* このプログラムのVERSION文字列を設定します。   {@value} */
    132        private static final String VERSION = "5.7.7.2 (2014/06/20)" ;
    133
    134        private static final long serialVersionUID = 577220140620L ;
    135
    136        private static final int ERROR_ROW_COUNT = 200 ;        // 4.0.0 (2007/05/25)
    137
    138        /** command 引数に渡す事の出来る コマンド  新規作成 {@value} */
    139        public static final String CMD_NEW   = "NEW" ;
    140        /** command 引数に渡す事の出来る コマンド  再検索 {@value} */
    141        public static final String CMD_RENEW = "RENEW" ;
    142
    143        private static final String[] COMMAND_LIST = new String[] { CMD_NEW , CMD_RENEW };
    144
    145        // 5.9.25.2 (2017/10/27)
    146        private final String CLOUD_STORAGE = HybsSystem.sys( "CLOUD_STORAGE");
    147        private final String CLOUD_CONTAINER = HybsSystem.sys("CLOUD_STORAGE_CONTAINER");
    148
    149
    150        private String  separator               = TableReader.TAB_SEPARATOR;     // 項目区切り文字
    151        private String  fileURL                 = HybsSystem.sys( "FILE_URL" );
    152        private String  filename                = HybsSystem.sys( "FILE_FILENAME"               );       // ファイル名
    153        private String  encode                  = HybsSystem.sys( "FILE_ENCODE"                 );       // ファイルエンコーディング  "JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS"
    154        private String  readerClass     = HybsSystem.sys( "TABLE_READER_DEFAULT_CLASS" );               // 3.8.5.3 (2006/08/07)
    155        private int             maxRowCount             = -1;
    156        private String  displayMsg              = HybsSystem.sys( "VIEW_DISPLAY_MSG" );
    157        private String  notfoundMsg             = "MSG0077";    // 対象データはありませんでした。
    158        private int     executeCount    = -1;                   // 検索/実行件数
    159        private String  modifyType              = null;
    160        private String adjustColumns    = null;         // 3.6.0.2 (2004/10/04) 取り込み時チェック
    161        private String checkColumns             = null;         // 3.6.0.2 (2004/10/04) 取り込み時チェック
    162        private String nullCheck                = null;         // 3.8.0.2 (2005/06/30) nullチェック確認
    163
    164        private transient DBTableModel table      = null;
    165        private String          command         = CMD_NEW;
    166        private String          tableId         = HybsSystem.TBL_MDL_KEY ;
    167        private String          sheetName       = null ;                // 3.5.4.2 (2003/12/15)
    168        private String          sheetNos        = null ;                // 5.5.7.2 (2012/10/09) 複数シートを指定できるようにシート番号を指定できるようにする。
    169        private String          sheetConstKeys  = null ;        // 5.5.8.2 (2012/11/09) 固定値となるカラム名(CSV形式)
    170        private String          sheetConstAdrs  = null ;        // 5.5.8.2 (2012/11/09) 固定値となるアドレス(行-列,行-列,・・・)
    171        private String          nullBreakClm    = null;         // 5.5.8.2 (2012/11/09) 取込み条件/Sheet BREAK条件
    172
    173        // 3.5.4.5 (2004/01/23) 外部よりカラム列(カンマ区切り)を指定できるようにする。
    174        private String          columns         = null;
    175        private boolean         useNumber       = true;                 // 3.7.0.5 (2005/04/11)
    176
    177        private boolean         stopZero        = false;                // 4.3.7.0 (2009/06/01) stopZero属性追加
    178
    179        // 5.1.8.0 (2010/07/01) AutoReaderのCalc対応
    180        private static final String[] AUTO_READER_CLASS  = new String[] { "Excel","Calc","Default"      ,"Default"     };
    181        private static final String[] AUTO_READER_ENCODE = new String[] { null   ,null  ,"UnicodeLittle","Windows-31J" };
    182
    183        private boolean isMainTrans             = true;                 // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
    184        private int             skipRowCount    = 0;                    // 5.1.6.0 (2010/05/01) データの読み飛ばし設定
    185
    186        // 5.9.8.1 (2016/05/13) 追加
    187        private String[]        matchKeys       = null;
    188        private String[]        matchVals       = null;
    189
    190        // 5.2.1.0 (2010/10/01) 読取処理でコードリソースのラベル変換を行うかどうか
    191        private boolean useRenderer             = HybsSystem.sysBool( "USE_TABLE_READER_RENDERER" );    // 5.2.1.0 (2010/10/01)
    192
    193        /**
    194         * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
    195         *
    196         * @og.rev 3.0.1.4 (2003/03/17) displayMsg が ０Byteの場合は、件数も表示しないように変更。
    197         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
    198         * @og.rev 3.5.4.1 (2003/12/01) 引数の BufferedReader を、InputStream に変更。
    199         * @og.rev 3.5.4.3 (2004/01/05) 引数の InputStream を、 BufferedReader に戻す。
    200         * @og.rev 3.5.6.5 (2004/08/09) 暫定的に、DBTableModelを先行削除します。
    201         * @og.rev 3.6.0.0 (2004/09/24) DBTableModel の先行削除は、scope="session" の場合のみ。
    202         * @og.rev 3.6.0.2 (2004/10/04) 取り込み時チェック用に、checkColumns,adjustColumns 属性追加
    203         * @og.rev 3.6.0.8 (2004/11/19) DBTableModel をセーブする時に、トランザクションチェックを行います。
    204         * @og.rev 3.8.5.3 (2006/08/07) readerClassが "Excel"でエラーが発生したとき、もう一度Defaultで再読取を行います。
    205         * @og.rev 4.0.0.0 (2007/10/12) checkTableColumn 前に、modifyType 設定を行います。
    206         * @og.rev 4.0.0.0 (2007/10/18) メッセージリソース統合( getResource().getMessage ⇒ getResource().getLabel )
    207         * @og.rev 4.3.1.1 (2008/10/08) columnsが指定されている場合は、AutoReader禁止
    208         * @og.rev 4.3.7.0 (2009/06/01) stopZero機能,DB.COUNTリクエストキーへ読込件数セットを追加
    209         * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
    210         * @og.rev 5.1.8.0 (2010/07/01) AutoReaderのCalc対応
    211         * @og.rev 5.1.9.0 (2010/08/01) AutoReaderでのExceptionの判定をThrowableに変更
    212         * @og.rev 5.7.1.2 (2013/12/20) tempMsg.toString() ⇒ errMsg 変更
    213         * @og.rev 5.7.7.2 (2014/06/20) caseKey,caseVal,caseNN,caseNull 属性を追加
    214         *
    215         * @return      後続処理の指示
    216         */
    217        @Override
    218        public int doEndTag() {
    219                debugPrint();           // 4.0.0 (2005/02/28)
    220                // 5.7.7.2 (2014/06/20) caseKey,caseVal,caseNN,caseNull 属性を追加
    221                if( !useTag() ) { return EVAL_PAGE ; }
    222
    223                if( check( command, COMMAND_LIST ) ) {
    224                        useMainTrans( isMainTrans );                    // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
    225                        startQueryTransaction( tableId );               // 3.6.0.8 (2004/11/19)
    226
    227                        // 3.5.6.5 (2004/08/09) 削除するのは、セッションのオブジェクトでよい。
    228                        // 3.6.0.0 (2004/09/24) 削除するのは、scope="session" の場合のみ。
    229                        if( "session".equals( getScope() ) ) {
    230                                removeSessionAttribute( tableId );
    231                                removeSessionAttribute( HybsSystem.VIEWFORM_KEY );
    232                        }
    233
    234                        if( maxRowCount < 0 ) {
    235                                maxRowCount     = sysInt( "DB_MAX_ROW_COUNT" );
    236                        }
    237
    238                        // ファイル の読み込み：AutoReader 処理
    239                        BufferedReader pw = null;
    240                        final String[] READER_CLASS  ;
    241                        final String[] READER_ENCODE ;
    242                        if( "AutoReader".equalsIgnoreCase( readerClass ) ) {
    243                                // 4.3.1.1 (2008/10/08)
    244                                if( columns != null && columns.length() > 0 ) {
    245                                        String errMsg = "columnsが指定されている場合は、readerClass=\"AutoReader\"は使えません";
    246                                        throw new HybsSystemException( errMsg ); // 4.3.4.4 (2009/01/01)
    247                                }
    248                                READER_CLASS  = AUTO_READER_CLASS ;
    249                                READER_ENCODE = AUTO_READER_ENCODE;
    250                        }
    251                        else {
    252                                READER_CLASS  = new String[] { readerClass };
    253                                READER_ENCODE = new String[] { encode };
    254                        }
    255
    256                        StringBuilder tempMsg = new StringBuilder();
    257                        for( int i=0; i<READER_CLASS.length; i++ ) {
    258                                readerClass = READER_CLASS[i];
    259                                encode      = READER_ENCODE[i];
    260
    261                                try {
    262                                        // 5.1.8.0 (2010/07/01) AutoReaderのCalc対応
    263                                        if( "Excel".equalsIgnoreCase( readerClass ) || "Calc".equalsIgnoreCase( readerClass ) ) {
    264                                                create( null );
    265                                        }
    266                                        else {
    267                                                pw = getBufferedReader();
    268                                                create( pw );
    269                                        }
    270                                        // 成功すれば、エラーメッセージをクリアして、その場で抜ける。
    271                                        tempMsg = null;
    272                                        break;
    273                                }
    274                                // 3.8.5.3 (2006/08/07) readerClassが "Excel"でエラーが発生したとき、もう一度Defaultで再読取を行います。
    275                                // 5.1.9.0 (2010/08/01) RuntimeException系のExceptionがキャッチできないため、Throwableで受ける
    276                                catch( Throwable th ) {
    277                                        tempMsg.append( "readerClass=["  ).append( readerClass )
    278                                                        .append( "],encode=["    ).append( encode )
    279                                                        .append( "] Error!"      ).append( HybsSystem.CR )
    280                                                        .append( th.getMessage() ).append( HybsSystem.CR ) ;
    281                                }
    282                                finally {
    283                                        Closer.ioClose( pw );           // 4.0.0 (2006/01/31) close 処理時の IOException を無視
    284                                }
    285                        }
    286
    287                        if( tempMsg != null ) { // 最後までエラーがあれば、例外処理を発行します。
    288                                String errMsg = tempMsg.toString();
    289                                System.err.print( errMsg );
    290                                throw new HybsSystemException( errMsg );                // 5.7.1.2 (2013/12/20) msg ⇒ errMsg 変更
    291                        }
    292
    293                        if( table != null ) {
    294                                // 3.6.0.2 (2004/10/04)
    295                                // 4.0.0.0 (2007/10/12) checkTableColumn 前に、modifyType 設定を行います。
    296                                executeCount = table.getRowCount();
    297                                if( modifyType != null ) {
    298                                        for( int row=0; row<executeCount; row++ ) {
    299                                                table.setModifyType( row,modifyType );
    300                                        }
    301                                }
    302
    303                                ErrorMessage errMsg = checkTableColumn( table );
    304                                if( errMsg != null && ! errMsg.isOK()) {
    305                                        jspPrint( TaglibUtil.makeHTMLErrorTable( errMsg,getResource() ) );
    306                                        return SKIP_PAGE ;
    307                                }
    308
    309                        }
    310                        // 3.6.0.8 (2004/11/19) トランザクションチェックを行います。
    311                        if( ! commitTableObject( tableId, table ) ) {
    312                                jspPrint( "ReadTableTag Query処理が割り込まれました。DBTableModel は登録しません。" );
    313                                return SKIP_PAGE ;
    314                        }
    315
    316                        StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_SMALL );
    317
    318                        // 実行件数の表示 command="NEW" のときのみ、displayMsg を表示させます。
    319                        // 4.0.0 (2005/11/30) 出力順の変更。一番最初に出力します。
    320                        if( CMD_NEW.equals( command ) ) {
    321                                if( executeCount > 0 && displayMsg != null && displayMsg.length() > 0 ) {
    322                                        buf.append( executeCount );
    323                                        buf.append( getResource().getLabel( displayMsg ) );
    324                                        buf.append( HybsSystem.BR );
    325                                }
    326                                else if( executeCount == 0 && notfoundMsg != null && notfoundMsg.length() > 0 ) {
    327                                        buf.append( getResource().getLabel( notfoundMsg ) );
    328                                        buf.append( HybsSystem.BR );
    329                                }
    330                        }
    331
    332                        // 4.3.7.0 (2009/06/01) 読込件数を、"DB.COUNT" キーでリクエストにセットする。
    333                        setRequestAttribute( "DB.COUNT"   , String.valueOf( executeCount ) );
    334
    335                        jspPrint( buf.toString() );
    336                }
    337
    338                // 4.3.7.0 (2009/06/01) stopZero機能を追加
    339                final int rtnCode ;
    340                if( executeCount == 0 && stopZero )     {
    341                        rtnCode = SKIP_PAGE;
    342                }
    343                else {
    344                        rtnCode = EVAL_PAGE;
    345                }
    346
    347                return rtnCode ;
    348        }
    349
    350        /**
    351         * タグリブオブジェクトをリリースします。
    352         * キャッシュされて再利用されるので、フィールドの初期設定を行います。
    353         *
    354         * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加
    355         * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
    356         * @og.rev 3.1.3.0 (2003/04/10) FILE_ENCODE から、エンコード情報を取得する。
    357         * @og.rev 3.1.4.0 (2003/04/18) command 属性に、初期値(NEW)を設定する。
    358         * @og.rev 3.5.4.2 (2003/12/15) EXCELのシート名を指定できるように変更。
    359         * @og.rev 3.5.4.5 (2004/01/23) 外部よりカラム列(カンマ区切り)を指定できるようにする。
    360         * @og.rev 3.6.0.2 (2004/10/04) checkColumns,adjustColumns,allColumnCheck 属性追加
    361         * @og.rev 3.7.0.5 (2005/04/11) useNumber 属性を追加します。
    362         * @og.rev 3.8.0.2 (2005/06/30) nullCheck 属性追加
    363         * @og.rev 3.8.5.3 (2006/08/07) readerClass 属性の初期値をシステムリソースより取得します。
    364         * @og.rev 4.3.7.0 (2009/06/01) stopZero属性追加
    365         * @og.rev 5.1.6.0 (2010/05/01) DBLastSqlの処理は、DBTableModelが新規作成された処理でのみ行う。
    366         * @og.rev 5.1.6.0 (2010/05/01) データの読み飛ばし設定 skipRowCount 属性追加
    367         * @og.rev 5.2.1.0 (2010/10/01) 読取処理でコードリソースのラベル変換を行うかどうか useRenderer 属性追加
    368         * @og.rev 5.5.7.2 (2012/10/09) 複数シートを指定できるようにシート番号を指定できるように、sheetNos属性追加
    369         * @og.rev 5.5.8.2 (2012/11/09) 固定値となるカラム名、アドレスの指定のための、sheetConstKeys、sheetConstAdrs属性追加
    370         * @og.rev 5.5.8.2 (2012/11/09) カラム列に NULL が現れた時点で読み取りを中止する、nullBreakClm属性追加
    371         * @og.rev 5.9.8.1 (2016/05/13) 読込条件の追加
    372         */
    373        @Override
    374        protected void release2() {
    375                super.release2();
    376                separator               = TableReader.TAB_SEPARATOR;   // 項目区切り文字
    377                fileURL                 = HybsSystem.sys( "FILE_URL" );
    378                filename                = HybsSystem.sys( "FILE_FILENAME" );   // ファイル名
    379                encode                  = HybsSystem.sys( "FILE_ENCODE"   );   // ファイルエンコーディング  "JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS"
    380                readerClass             = HybsSystem.sys( "TABLE_READER_DEFAULT_CLASS" );               // 3.8.5.3 (2006/08/07)
    381                maxRowCount             = -1;
    382                displayMsg              = HybsSystem.sys( "VIEW_DISPLAY_MSG" );
    383                notfoundMsg             = "MSG0077";    // 対象データはありませんでした。
    384                executeCount    = -1;                   // 検索/実行件数
    385                modifyType              = null;
    386                command                 = CMD_NEW;
    387                table                   = null;
    388                tableId                 = HybsSystem.TBL_MDL_KEY ;
    389                sheetName               = null;         // 3.5.4.2 (2003/12/15)
    390                sheetNos                = null ;        // 5.5.7.2 (2012/10/09) 複数シートを指定できるようにシート番号を指定できるようにする。
    391                sheetConstKeys  = null ;        // 5.5.8.2 (2012/11/09) 固定値となるカラム名(CSV形式)
    392                sheetConstAdrs  = null ;        // 5.5.8.2 (2012/11/09) 固定値となるアドレス(行-列,行-列,・・・)
    393                nullBreakClm    = null;         // 5.5.8.2 (2012/11/09) 取込み条件/Sheet BREAK条件
    394                columns                 = null;         // 3.5.4.5 (2004/01/23)
    395                useNumber               = true;         // 3.7.0.5 (2005/04/11)
    396                adjustColumns   = null;         // 3.6.0.2 (2004/10/04) 取り込み時チェック
    397                checkColumns    = null;         // 3.6.0.2 (2004/10/04) 取り込み時チェック
    398                nullCheck               = null;         // 3.8.0.2 (2005/06/30)
    399                stopZero                = false;        // 4.3.7.0 (2009/06/01) soptZero追加
    400                isMainTrans             = true;         // 5.1.6.0 (2010/05/01) DBLastSqlの処理の見直し
    401                skipRowCount    = 0;            // 5.1.6.0 (2010/05/01) データの読み飛ばし設定
    402                matchKeys               = null;         // 5.9.8.1 (2016/05/13) 読込条件設定
    403                matchVals               = null;         // 5.9.8.1 (2016/05/13) 読込条件設定
    404                useRenderer             = HybsSystem.sysBool( "USE_TABLE_READER_RENDERER" );    // 5.2.1.0 (2010/10/01)
    405        }
    406
    407        /**
    408         * TableReader の実オブジェクトを生成して，BufferedReader に書き込みます。
    409         *
    410         * @og.rev 3.5.4.1 (2003/12/01) 引数の BufferedReader を、InputStream に変更。
    411         * @og.rev 3.5.4.2 (2003/12/15) TableReader のサブクラス名変更。
    412         * @og.rev 3.5.4.2 (2003/12/15) EXCELのシート名を指定できるように変更。
    413         * @og.rev 3.5.4.3 (2004/01/05) 引数の InputStream を、 BufferedReader に戻す。
    414         * @og.rev 3.5.4.5 (2004/01/23) TableReader に、encode を渡すように変更。
    415         * @og.rev 3.5.6.0 (2004/06/18) 各種プラグイン関連付け設定を、システムパラメータ に記述します。
    416         * @og.rev 3.7.0.5 (2005/04/11) useNumber 属性を追加します。
    417         * @og.rev 4.0.0.0 (2005/01/31) キーの指定を、TableReader. から、TableReader_ に変更します。
    418         * @og.rev 4.0.0.0 (2005/01/31) lang ⇒ ResourceManager へ変更
    419         * @og.rev 5.1.6.0 (2010/05/01) データの読み飛ばし設定 skipRowCount 属性追加
    420         * @og.rev 5.2.1.0 (2010/10/01) 読取処理でコードリソースのラベル変換を行うかどうか設定 useRenderer 属性追加
    421         * @og.rev 5.5.7.2 (2012/10/09) 複数シートを指定できるようにシート番号を指定できるように、sheetNos属性追加
    422         * @og.rev 5.5.8.2 (2012/11/09) 固定値となるカラム名、アドレスの指定のための、sheetConstKeys、sheetConstAdrs属性追加
    423         * @og.rev 5.5.8.2 (2012/11/09) カラム列に NULL が現れた時点で読み取りを中止する、nullBreakClm属性追加
    424         * @og.rev 5.9.8.1 (2016/05/13) match対応
    425         * @og.rev 5.9.25.2 (2017/10/27) クラウドストレージ対応
    426         *
    427         * @param       out     出力するBufferedReaderオブジェクト
    428         */
    429        protected void create( final BufferedReader out )  {
    430                String className = HybsSystem.sys( "TableReader_" + readerClass ) ;             // 4.0.0 (2005/01/31)
    431                TableReader reader = (TableReader)HybsSystem.newInstance( className );  // 3.5.5.3 (2004/04/09)
    432                reader.setResourceManager( getResource() );     // 4.0.0 (2005/01/31)
    433                reader.setSeparator( separator );
    434                reader.setEncode( encode );                                             // 3.5.4.5 (2004/01/23)
    435                reader.setColumns( columns );                                   // 3.5.4.5 (2004/01/23)
    436                reader.setUseNumber( useNumber );                               // 3.7.0.5 (2005/04/11)
    437                reader.setMaxRowCount( maxRowCount );
    438                reader.setSkipRowCount( skipRowCount );                 // 5.1.6.0 (2010/05/01)
    439                reader.setUseRenderer( useRenderer );                   // 5.2.1.0 (2010/10/01)
    440                reader.setMatchKeys( matchKeys );                               // 5.9.8.1 (2016/05/13)
    441                reader.setMatchVals( matchVals );                               // 5.9.8.1 (2016/05/13)
    442                reader.setDebug( isDebug() );                                   // 5.5.7.2 (2012/10/09) デバッグ情報を出力するかどうかを指定
    443                if( reader.isExcel() ) {                                                // 3.5.4.3 (2004/01/05)
    444                        // 5.9.25.2 (2017/10/27)
    445                        String filePath = "";
    446                        try{
    447                                if(CLOUD_STORAGE != null && CLOUD_STORAGE.length() > 0){
    448                                        // クラウド上のファイルをサーバにダウンロードする。
    449                                        filePath = downloadStorageFile(CLOUD_STORAGE, CLOUD_CONTAINER);
    450                                }
    451
    452                                reader.setFilename( HybsSystem.url2dir( StringUtil.urlAppend( fileURL,filename )));
    453                                reader.setSheetName( sheetName );                       // 3.5.4.2 (2003/12/15)
    454                                reader.setSheetNos( sheetNos );                         // 5.5.7.2 (2012/10/09) 複数シートを指定できるようにシート番号を指定できるようにする。
    455                                reader.setSheetConstData( sheetConstKeys,sheetConstAdrs ) ;             // 5.5.8.2 (2012/11/09) 固定値となるカラム名、アドレスの指定
    456                                reader.setNullBreakClm( nullBreakClm ) ;        // 5.5.8.2 (2012/11/09) 取込み条件/Sheet BREAK条件
    457                                reader.readDBTable();
    458
    459                        // 5.9.25.2 (2017/10/27)
    460                        }finally{
    461                                if(CLOUD_STORAGE != null && CLOUD_STORAGE.length() > 0){
    462                                        // サーバにダウンロードしたファイルを削除
    463                                        File file = new File(filePath);
    464                                        if(file.exists()){
    465                                                file.delete();
    466                                        }
    467                                }
    468                        }
    469                }
    470                else {
    471                        reader.readDBTable( out );
    472                }
    473                table = reader.getDBTableModel();
    474        }
    475
    476        /**
    477         * クラウドストレージのExcelファイルをサーバにダウンロード。
    478         * (POIで処理する必要があるため)
    479         *
    480         * @og.rev 5.9.25.2 (2017/10/27) 新規作成
    481         *
    482         * @param storage クラウドのストレージ
    483         * @param container クラウドのコンテナ名
    484         * @return filePath サーバのファイルパス
    485         */
    486        private String downloadStorageFile(String storage, String container){
    487                String filePath = HybsSystem.url2dir( StringUtil.urlAppend( fileURL,filename ));
    488                HttpServletRequest request = (HttpServletRequest)getRequest();
    489                HttpSession hsession = request.getSession(true);
    490                StorageAPI storageApi = StorageAPIFactory.newStorageAPI(storage, container, hsession);
    491                // ｽﾄﾚｰｼﾞからﾀﾞｳﾝﾛｰﾄﾞ
    492                InputStream is = storageApi.get(fileURL + filename, hsession);
    493
    494                try{
    495                        // ｺﾋﾟｰ先ﾌｧｲﾙﾊﾟｽ
    496                        File file = new File(filePath);
    497
    498                        // 既にﾌｧｲﾙが存在する場合は、削除
    499                        if(file.exists()){
    500                                file.delete();
    501                        }
    502
    503                        // ｽﾄﾚｰｼﾞからｻｰﾊﾞにﾌｧｲﾙｺﾋﾟｰ
    504                        Files.copy(is, file.toPath());
    505
    506                }catch(IOException e){
    507                        String errMsg = "download error：" + e;
    508                        throw new HybsSystemException(errMsg);
    509                }
    510
    511                return filePath;
    512        }
    513
    514        /**
    515         * BufferedReader を取得します。
    516         *
    517         * ここでは、一般的なファイル出力を考慮した BufferedReader を作成します。
    518         *
    519         * @og.rev 2.2.0.0 (2002/12/17) 中国語(国際化)対応 エンコードの取得方法変更
    520         * @og.rev 3.1.3.0 (2003/04/10) FILE_ENCODE から、エンコード情報を取得する。
    521         * @og.rev 3.5.4.1 (2003/12/01) 引数の BufferedReader を、InputStream に変更。
    522         * @og.rev 3.5.4.3 (2004/01/05) 引数の InputStream を、 BufferedReader に戻す。
    523         * @og.rev 3.5.5.9 (2004/06/07) FileUtil.getBufferedReader を使用
    524         * @og.rev 5.9.25.2 (2017/10/27) クラウドストレージ対応
    525         *
    526         * @return      ファイル読取BufferedReaderオブジェクト
    527         */
    528        private BufferedReader getBufferedReader() {
    529                if( filename == null ) {
    530                        String errMsg = "ファイル名がセットされていません。";
    531                        throw new HybsSystemException( errMsg );
    532                }
    533
    534                // 2017/10/20 DELETE 後の判定処理内に移動
    535                // 通常の処理
    536//              String directory = HybsSystem.url2dir( fileURL );
    537//              File file = new File( StringUtil.urlAppend( directory,filename ) );
    538//
    539//              BufferedReader out = FileUtil.getBufferedReader( file,encode );
    540
    541                // 5.9.25.2 (2017/10/27) クラウド対応
    542
    543                BufferedReader out = null;
    544                if(CLOUD_STORAGE != null && CLOUD_STORAGE.length() > 0){
    545                        // クラウドの場合の処理
    546                        HttpServletRequest request = (HttpServletRequest)getRequest();
    547                        HttpSession hsession = request.getSession(true);
    548                        StorageAPI storageAPI = StorageAPIFactory.newStorageAPI(CLOUD_STORAGE, CLOUD_CONTAINER, hsession);
    549                        // クラウドのオブジェクトを読み込み
    550                        InputStream is = storageAPI.get(fileURL + filename, hsession);
    551                        try{
    552                                out = new BufferedReader(new InputStreamReader(is, encode));
    553                        }catch(UnsupportedEncodingException e){
    554                                String errMsg = "サポートされていない文字コードです。encode:" + encode;
    555                                throw new HybsSystemException(errMsg);
    556                        }
    557                }else{
    558                        // 通常の処理
    559                        String directory = HybsSystem.url2dir( fileURL );
    560                        File file = new File( StringUtil.urlAppend( directory,filename ) );
    561
    562                        out = FileUtil.getBufferedReader( file,encode );
    563                }
    564
    565                return out ;
    566        }
    567
    568        /**
    569         * カラム文字列(CSV形式)から、カラム番号配列を作成します。
    570         * 簡易メソッドです。
    571         * 引数が、"*" の場合は、全カラムを指定したことになります。
    572         * null の場合は、サイズが ０ の配列を返します。
    573         *
    574         * @og.rev 4.0.0.0 (2007/05/25) 新規作成
    575         *
    576         * @param       clms    カラム文字列(CSV形式)
    577         * @param       table   DBTableModelオブジェクト
    578         *
    579         * @return      カラム番号配列(無い場合は、長さ０の配列)
    580         */
    581        private int[] makeClmNos( final String clms,final DBTableModel table ) {
    582                final int[] clmNo;
    583
    584                if( clms == null ) {
    585                        clmNo = new int[0];
    586                }
    587                else if( "*".equals( clms ) ) {
    588                        int size = table.getColumnCount();
    589                        clmNo = new int[size];
    590                        for( int i=0; i<size; i++ ) {
    591                                clmNo[i] = i;
    592                        }
    593                }
    594                else {
    595                        String[] clmStr = StringUtil.csv2Array( clms );
    596                        int size = clmStr.length;
    597                        clmNo = new int[size];
    598                        for( int i=0; i<size; i++ ) {
    599                                clmNo[i] = table.getColumnNo( clmStr[i] );
    600                        }
    601                }
    602
    603                return clmNo;
    604        }
    605
    606        /**
    607         * checkColumns に指定されたカラムをチェックします。
    608         * カラムオブジェクトのDBType属性に対応したチェックを行います。
    609         * チェック結果で、エラーが発生した場合は、ErrorMessage オブジェクトを
    610         * 返します。
    611         * DBColumn#valueCheck( String ) の結果のErrorMessageをすべて append
    612         * していきます。
    613         * useAdjust==true で、かつ、エラーがない場合は、adjustColumns 処理結果を
    614         * DBTableModel に反映させます。
    615         * debug=true で、エラー時の詳細なデータを出力します。
    616         *
    617         * @og.rev 3.6.0.2 (2004/10/04) 新規作成
    618         * @og.rev 3.8.0.2 (2005/06/30) nullチェック確認
    619         * @og.rev 4.0.0.0 (2007/05/25) 処理順序書き換え
    620         *
    621         * @param       table   DBTableModelオブジェクト
    622         *
    623         * @return      カラムキー ＋ 値 のエラーメッセージオブジェクト
    624         */
    625        private ErrorMessage checkTableColumn( final DBTableModel table ) {
    626                ErrorMessage errMsg = new ErrorMessage( "Check Columns Error!" );
    627
    628                int rowCnt = table.getRowCount();
    629                int[] adjClmNo = makeClmNos( adjustColumns,table );
    630                int[] chkClmNo = makeClmNos( checkColumns,table );
    631                int[] nllclmNo = makeClmNos( nullCheck,table );
    632
    633                boolean useAdjust = adjClmNo.length > 0 ;
    634
    635                for( int row=0; row<rowCnt; row++ ) {
    636                        String[]   vals   = table.getValues( row );
    637                        DBColumn[] dbClms = table.getDBColumns();
    638                        boolean isError = false;                                                // 5.5.7.2 (2012/10/09) エラー時のフラグ。ループでクリアする。
    639
    640                        // adjustColumns 処理
    641                        for( int i=0; i<adjClmNo.length; i++ ) {
    642                                int no = adjClmNo[i];
    643                                vals[no] = dbClms[no].valueSet( vals[no] );
    644                        }
    645
    646                        // checkColumns 処理
    647                        for( int i=0; i<chkClmNo.length; i++ ) {
    648                                int no = chkClmNo[i];
    649                                ErrorMessage msg = dbClms[no].valueCheck( vals[no] );
    650                                if( msg.getKekka() > ErrorMessage.OK ) {
    651                                        isError = true;
    652                                        errMsg.append( row+1,dbClms[no].valueCheck( vals[no] ) );
    653                                }
    654                        }
    655
    656                        // nullCheck 処理
    657                        for( int i=0; i<nllclmNo.length; i++ ) {
    658                                int no = nllclmNo[i];
    659                                if( vals[no] == null || vals[no].length() == 0 ) {
    660                                        isError = true;
    661                                        String label = dbClms[no].getLabel();
    662                                        // ERR0012 : 指定のデータがセットされていません。(NULLエラー)。key={0}
    663                                        errMsg.addMessage( row+1,ErrorMessage.NG,"ERR0012",label );
    664                                }
    665                        }
    666
    667                        // 5.5.7.2 (2012/10/09) エラー時のデバッグ出力
    668                        if( isDebug() && isError ) {
    669                                errMsg.addMessage( row+1,ErrorMessage.OK,"Debug Info",java.util.Arrays.toString(table.getValues(row) ) );
    670                        }
    671
    672                        // adjustColumns 処理結果を反映させます。
    673                        if( useAdjust && !isError ) { table.setValues( vals,row ); }
    674                        if( errMsg.size() > ERROR_ROW_COUNT ) { break; }
    675                }
    676
    677                return errMsg;
    678        }
    679
    680        /**
    681         * 【TAG】(通常は使いません)結果のDBTableModelを、sessionに登録するときのキーを指定します
    682         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY}])。
    683         *
    684         * @og.tag
    685         * 検索結果より、DBTableModelオブジェクトを作成します。これを、下流のviewタグ等に
    686         * 渡す場合に、通常は、session を利用します。その場合の登録キーです。
    687         * query タグを同時に実行して、結果を求める場合、同一メモリに配置される為、
    688         * この tableId 属性を利用して、メモリ空間を分けます。
    689         *              (初期値:HybsSystem#TBL_MDL_KEY[={@og.value org.opengion.hayabusa.common.HybsSystem#TBL_MDL_KEY}])。
    690         *
    691         * @param       id sessionに登録する時の ID
    692         */
    693        public void setTableId( final String id ) {
    694                tableId = nval( getRequestParameter( id ), tableId );
    695        }
    696
    697        /**
    698         * 【TAG】可変長ファイルを作成するときの項目区切り文字をセットします。
    699         *
    700         * @og.tag 可変長ファイルを作成するときの項目区切り文字をセットします。
    701         *
    702         * @param   separator 項目区切り文字
    703         */
    704        public void setSeparator( final String separator ) {
    705                this.separator = nval( getRequestParameter( separator ),this.separator );
    706        }
    707
    708        /**
    709         * 【TAG】読み取り元ディレクトリ名を指定します
    710         *              (初期値:FILE_URL[={@og.value org.opengion.hayabusa.common.SystemData#FILE_URL}])。
    711         *
    712         * @og.tag
    713         * この属性で指定されるディレクトリより、ファイルを読み取ります。
    714         * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、２文字目が、
    715         * ":" (Windows)の場合は、指定のURLそのままのディレクトリに、そうでない場合は、
    716         * fileURL = "{&#064;USER.ID}" と指定すると、FILE_URL 属性で指定のフォルダの下に、
    717         * さらに、各個人ID別のフォルダを作成して、そこを操作します。
    718         * (初期値:システム定数のFILE_URL[={@og.value org.opengion.hayabusa.common.SystemData#FILE_URL}])。
    719         *
    720         * @og.rev 4.0.0.0 (2005/01/31) StringUtil.urlAppend メソッドの利用
    721         * @og.rev 4.0.0.0 (2007/11/20) 指定されたディレクトリ名の最後が"\"or"/"で終わっていない場合に、"/"を付加する。
    722         *
    723         * @param       url 読み取り元ディレクトリ名
    724         * @see         org.opengion.hayabusa.common.SystemData#FILE_URL
    725         */
    726        public void setFileURL( final String url ) {
    727                String furl = nval( getRequestParameter( url ),null );
    728                if( furl != null ) {
    729                        char ch = furl.charAt( furl.length()-1 );
    730                        if( ch != '/' && ch != '\\' ) { furl = furl + "/"; }
    731                        fileURL = StringUtil.urlAppend( fileURL,furl );
    732                }
    733        }
    734
    735        /**
    736         * 【TAG】ファイルを作成するときのファイル名をセットします
    737         *              (初期値:FILE_FILENAME[={@og.value org.opengion.hayabusa.common.SystemData#FILE_FILENAME}])。
    738         *
    739         * @og.tag ファイルを作成するときのファイル名をセットします。
    740         * (初期値:システム定数のFILE_FILENAME[={@og.value org.opengion.hayabusa.common.SystemData#FILE_FILENAME}])。
    741         *
    742         * @param   filename ファイル名
    743         * @see         org.opengion.hayabusa.common.SystemData#USE_SQL_INJECTION_CHECK
    744         */
    745        public void setFilename( final String filename ) {
    746                this.filename = nval( getRequestParameter( filename ),this.filename );
    747        }
    748
    749        /**
    750         * 【TAG】ファイルを作成するときのファイルエンコーディング名をセットします
    751         *              (初期値:FILE_ENCODE[={@og.value org.opengion.hayabusa.common.SystemData#FILE_ENCODE}])。
    752         *
    753         * @og.tag
    754         * Shift_JIS,MS932,Windows-31J,UTF-8,ISO-8859-1,UnicodeLittle
    755         * (初期値:システム定数のFILE_ENCODE[={@og.value org.opengion.hayabusa.common.SystemData#FILE_ENCODE}])。
    756         *
    757         * @og.rev 2.2.0.0 (2002/12/17) 中国語(国際化)対応 エンコードの取得方法変更
    758         * @og.rev 3.1.3.0 (2003/04/10) FILE_ENCODE から、エンコード情報を取得する。
    759         *
    760         * @param   enc ファイルエンコーディング名
    761         * @see     <a href="http://www.iana.org/assignments/character-sets">IANA Charset Registry</a>
    762         * @see         org.opengion.hayabusa.common.SystemData#FILE_ENCODE
    763         */
    764        public void setEncode( final String enc ) {
    765                encode = nval( getRequestParameter( enc ),encode );
    766        }
    767
    768        /**
    769         * 【TAG】実際に読み出すクラス名の略称(TableReader_**** の ****)をセットします({@og.doc03Link readerClass 初期値:Default})。
    770         *
    771         * @og.tag
    772         * 実際に読み出すクラス名(の略称)をセットします。
    773         * これは、org.opengion.hayabusa.io 以下の TableReader_**** クラスの **** を
    774         * 与えます。これらは、TableReader インターフェースを継承したサブクラスです。
    775         * 属性クラス定義の {@link org.opengion.hayabusa.io.TableReader TableReader} を参照願います。
    776         * {@og.doc03Link readerClass TableReader_**** クラス}
    777         *
    778         * @param   readerClass クラス名(の略称)
    779         * @see         org.opengion.hayabusa.io.TableReader  TableReaderのサブクラス
    780         */
    781        public void setReaderClass( final String readerClass ) {
    782                this.readerClass = nval( getRequestParameter( readerClass ),this.readerClass );
    783        }
    784
    785        /**
    786         * 【TAG】読取時の最大取り込み件数をセットします
    787         *              (初期値:DB_MAX_ROW_COUNT[={@og.value org.opengion.hayabusa.common.SystemData#DB_MAX_ROW_COUNT}])。
    788         *
    789         * @og.tag
    790         * DBTableModelのデータとして登録する最大件数をこの値に設定します。
    791         * サーバーのメモリ資源と応答時間の確保の為です。
    792         * 0 をセットすると、無制限(Integer.MAX_VALUE)になります。
    793         * (初期値:ユーザー定数のDB_MAX_ROW_COUNT[={@og.value org.opengion.hayabusa.common.SystemData#DB_MAX_ROW_COUNT}])。
    794         *
    795         * @og.rev 5.5.8.5 (2012/11/27) 0を無制限として処理します。
    796         *
    797         * @param   count 読取時の最大取り込み件数
    798         * @see         org.opengion.hayabusa.common.SystemData#DB_MAX_ROW_COUNT
    799         */
    800        public void setMaxRowCount( final String count ) {
    801                maxRowCount = nval( getRequestParameter( count ),maxRowCount );
    802                if( maxRowCount == 0 ) { maxRowCount = Integer.MAX_VALUE ; }            // 5.5.8.5 (2012/11/27)
    803        }
    804
    805        /**
    806         * 【TAG】コマンド(NEW,RENEW)をセットします(初期値:NEW)。
    807         *
    808         * @og.tag
    809         * コマンドは,HTMLから(get/post)指定されますので,CMD_xxx で設定される
    810         * フィールド定数値のいづれかを、指定できます。
    811         * 何も設定されない、または、null の場合は、"NEW" が初期値にセットされます。
    812         *
    813         * @param       cmd コマンド(public static final 宣言されている文字列)
    814         * @see         <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.ReadTableTag.CMD_NEW">コマンド定数</a>
    815         */
    816        public void setCommand( final String cmd ) {
    817                String cmd2 = getRequestParameter( cmd );
    818                if( cmd2 != null && cmd2.length() > 0 ) { command = cmd2.toUpperCase(Locale.JAPAN); }
    819        }
    820
    821        /**
    822         * 【TAG】query の結果を画面上に表示するメッセージIDを指定します(初期値:MSG0033[　件検索しました])。
    823         *
    824         * @og.tag
    825         * ここでは、検索結果の件数や登録された件数をまず出力し、
    826         * その次に、ここで指定したメッセージをリソースから取得して
    827         * 表示します。
    828         * 表示させたくない場合は, displayMsg = "" をセットしてください。
    829         * 初期値は、検索件数を表示します。
    830         *
    831         * @param   id ディスプレイに表示させるメッセージ ID
    832         */
    833        public void setDisplayMsg( final String id ) {
    834                if( id != null ) { displayMsg = id; }
    835        }
    836
    837        /**
    838         * 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])。
    839         *
    840         * @og.tag
    841         * ここでは、検索結果がゼロ件の場合のみ、特別なメッセージを表示させます。
    842         * 従来は、displayMsg と兼用で、『0　件検索しました』という表示でしたが、
    843         * displayMsg の初期表示は、OFF になりましたので、ゼロ件の場合のみ別に表示させます。
    844         * 表示させたくない場合は, notfoundMsg = "" をセットしてください。
    845         * 初期値は、MSG0077[対象データはありませんでした]です。
    846         *
    847         * @param       id ディスプレイに表示させるメッセージ ID
    848         */
    849        public void setNotfoundMsg( final String id ) {
    850                String ids = getRequestParameter( id );
    851                if( ids != null ) { notfoundMsg = ids; }
    852        }
    853
    854        /**
    855         * 【TAG】ファイル取り込み時の モディファイタイプ(A(追加),C(更新),D(削除))を指定します。
    856         *
    857         * @og.tag
    858         * ファイル読み込み時に、そのデータをA(追加)、C(更新)、D(削除)の
    859         * モディファイタイプをつけた状態にします。
    860         * その状態で、そのまま、update する事が可能になります。
    861         *
    862         * @param   type ファイル取り込み時の モディファイタイプ(A,C,D属性)
    863         */
    864        public void setModifyType( final String type ) {
    865                modifyType = getRequestParameter( type );
    866        }
    867
    868        /**
    869         * 【TAG】EXCELファイルを読み込むときのシート名を設定します(初期値:指定なし)。
    870         *
    871         * @og.tag
    872         * EXCELファイルを読み込む時に、シート名を指定します。これにより、複数の形式の
    873         * 異なるデータを順次読み込むことや、シートを指定して読み取ることが可能になります。
    874         * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。
    875         * 初期値は、指定なしです。
    876         *
    877         * @og.rev 3.5.4.2 (2003/12/15) 新規追加
    878         *
    879         * @param   sheet EXCELファイルのシート名
    880         * @see         #setSheetNos( String )
    881         */
    882        public void setSheetName( final String sheet ) {
    883                sheetName = nval( getRequestParameter( sheet ),sheetName );
    884        }
    885
    886        /**
    887         * 【TAG】EXCELファイルを読み込むときのシート番号を指定します(初期値:0)。
    888         *
    889         * @og.tag
    890         * EXCEL読み込み時に複数シートをマージして取り込みます。
    891         * シート番号は、0 から始まる数字で表します。
    892         * ヘッダーは、最初のシートのカラム位置に合わせます。（ヘッダータイトルの自動認識はありません。）
    893         * よって、指定するシートは、すべて同一レイアウトでないと取り込み時にカラムのずれが発生します。
    894         *
    895         * シート番号の指定は、カンマ区切りで、複数指定できます。また、N-M の様にハイフンで繋げることで、
    896         * N 番から、M 番のシート範囲を一括指定可能です。また、"*" による、全シート指定が可能です。
    897         * これらの組み合わせも可能です。（ 0,1,3,5-8,10-* ）
    898         * ただし、"*" に関しては例外的に、一文字だけで、すべてのシートを表すか、N-* を最後に指定するかの
    899         * どちらかです。途中には、"*" は、現れません。
    900         * シート番号は、重複(1,1,2,2)、逆転(3,2,1) での指定が可能です。これは、その指定順で、読み込まれます。
    901         * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。
    902         *
    903         * 初期値は、0（第一シート） です。
    904         *
    905         * @og.rev 5.5.7.2 (2012/10/09) 新規追加
    906         *
    907         * @param   sheet EXCELファイルのシート番号（0から始まる）
    908         * @see         #setSheetName( String )
    909         */
    910        public void setSheetNos( final String sheet ) {
    911                sheetNos = nval( getRequestParameter( sheet ),sheetNos );
    912                if( sheetNos != null && sheetNos.length() > 0 ) {
    913                        boolean errFlag = false;
    914                        for( int i=0; i<sheetNos.length(); i++ ) {
    915                                char ch = sheetNos.charAt(i);
    916                                if( ch == '-' || ch == ',' ) { continue; }
    917                                if( ch == '*' && ( i==0 || i==sheetNos.length()-1 ) ) { continue; }
    918                                if( ch < '0' || ch > '9' ) { errFlag = true; break; }
    919                        }
    920                        if( errFlag ) {
    921                                String errMsg = "sheetNos の指定を見直してください。sheetNos=[" + sheetNos + "]";
    922                                throw new HybsSystemException( errMsg );
    923                        }
    924                }
    925        }
    926
    927        /**
    928         * 【TAG】EXCELファイルを読み込むときのシート単位の固定値を設定するためのカラム名を指定します。
    929         *
    930         * @og.tag
    931         * カラム名は、カンマ区切りで指定します。
    932         * これにより、シートの一か所に書かれている情報を、DBTableModel のカラムに固定値として
    933         * 設定することができます。
    934         * 例として、DB定義書で、テーブル名をシートの全レコードに設定したい場合などに使います。
    935         * このメソッドは、isExcel() == true の場合のみ利用されます。
    936         *
    937         * @og.rev 5.5.8.2 (2012/11/09) 新規追加
    938         *
    939         * @param   constKeys 固定値となるカラム名(CSV形式)
    940         * @see         #setSheetConstAdrs( String )
    941         */
    942        public void setSheetConstKeys( final String constKeys ) {
    943                sheetConstKeys = nval( getRequestParameter( constKeys ),null );
    944        }
    945
    946        /**
    947         * 【TAG】EXCELファイルを読み込むときのシート単位の固定値を設定するためのカラム名に対応するアドレスを指定します。
    948         *
    949         * @og.tag
    950         * アドレスは、EXCEL上の行-列をカンマ区切りで指定します。
    951         * 行列は、EXCELオブジェクトに準拠するため、０から始まる整数です。
    952         * 0-0 ⇒ A1 , 1-0 ⇒ A2 , 0-1 ⇒ B1 になります。
    953         * これにより、シートの一か所に書かれている情報を、DBTableModel のカラムに固定値として
    954         * 設定することができます。
    955         * 例として、DB定義書で、テーブル名をシートの全レコードに設定したい場合などに使います。
    956         * このメソッドは、isExcel() == true の場合のみ利用されます。
    957         *
    958         * 5.7.6.3 (2014/05/23) より、
    959         *   ①EXCEL表記に準拠した、A1,A2,B1 の記述も処理できるように対応します。
    960         *     なお、A1,A2,B1 の記述は、必ず、英字1文字＋数字 にしてください。(A～Zまで)
    961         *   ②処理中のEXCELシート名をカラムに割り当てるために、"SHEET" という記号に対応します。
    962         * 例えば、sheetConstKeys="CLM,LANG,NAME" とし、sheetConstAdrs="0-0,A2,SHEET" とすると、
    963         * NAMEカラムには、シート名を読み込むことができます。
    964         * これは、内部処理の簡素化のためです。
    965         *
    966         * ちなみに、EXCELのセルに、シート名を表示させる場合の関数は、下記の様になります。
    967         * =RIGHT(CELL("filename",$A$1),LEN(CELL("filename",$A$1))-FIND("]",CELL("filename",$A$1)))
    968         *
    969         * @og.rev 5.5.8.2 (2012/11/09) 新規追加
    970         *
    971         * @param   constAdrs 固定値となるアドレス(行-列,行-列,・・・)
    972         * @see         #setSheetConstKeys( String )
    973         */
    974        public void setSheetConstAdrs( final String constAdrs ) {
    975                sheetConstAdrs = nval( getRequestParameter( constAdrs ),null );
    976        }
    977
    978        /**
    979         * 【TAG】ここに指定されたカラム列に NULL が現れた時点で読み取りを中止します。
    980         *
    981         * @og.tag
    982         * これは、指定のカラムは必須という事を条件に、そのレコードだけを読み取る処理を行います。
    983         * 複数Sheetの場合は、次のSheetを読みます。
    984         * 現時点では、Excel の場合のみ有効です。
    985         *
    986         * @og.rev 5.5.8.2 (2012/11/09) 新規追加
    987         *
    988         * @param   clm カラム列
    989         */
    990        public void setNullBreakClm( final String clm ) {
    991                nullBreakClm = nval( getRequestParameter( clm ),null );
    992        }
    993
    994        /**
    995         * 【TAG】読み取り元ファイルのカラム列を、外部(タグ)より指定します。
    996         *
    997         * @og.tag
    998         * 読み取り元ファイルのカラム列を、外部(タグ)より指定します。
    999         * ファイルに記述された #NAME より優先して使用されます。
    1000         * これは、元ファイルのカラムを順番に指定のカラム名に割り当てる機能で
    1001         * ファイルの特定のカラム列を抜き出して取り込む機能ではありません。
    1002         *
    1003         * @og.rev 3.5.4.5 (2004/01/23) 新規作成
    1004         *
    1005         * @param   clms 読み取り元ファイルのカラム列(カンマ区切り文字)
    1006         */
    1007        public void setColumns( final String clms ) {
    1008                columns = nval( getRequestParameter( clms ),columns );
    1009        }
    1010
    1011        /**
    1012         * 【TAG】読み取り元ファイルの整合性チェックを行うカラム列をカンマ指定します。
    1013         *
    1014         * @og.tag
    1015         * カラムオブジェクトのDBType属性に対応したチェックを行います。
    1016         * 指定のカラム名をカンマ区切り(CSV)で複数指定できます。
    1017         * 全てのカラムのチェックを行う場合は、allColumnCheck = "true" を
    1018         * 指定して下さい。
    1019         * 分解方法は、通常のパラメータ取得後に、CSV分解します。
    1020         *
    1021         * @og.rev 3.6.0.2 (2004/10/04) 新規追加 取り込み時チェック用
    1022         * @og.rev 3.8.8.5 (2007/03/09) 通常のパラメータ取得後に、CSV分解に戻します。
    1023         *
    1024         * @param   clms 整合性チェックを行うカラム列(カンマ区切り文字)
    1025         */
    1026        public void setCheckColumns( final String clms ) {
    1027                checkColumns = nval( getRequestParameter( clms ),checkColumns );
    1028        }
    1029
    1030        /**
    1031         * 【TAG】読み取り元ファイルのデータ変換を行うカラム列をカンマ指定します。
    1032         *
    1033         * @og.tag
    1034         * カラムオブジェクトのDBType属性に対応したデータ変換を行います。
    1035         * 指定のカラム名をカンマ区切り(CSV)で複数指定できます。
    1036         * 分解方法は、通常のパラメータ取得後に、CSV分解します。
    1037         *
    1038         * @og.rev 3.6.0.2 (2004/10/04) 新規追加 取り込み時データ変換
    1039         * @og.rev 3.8.8.5 (2007/03/09) 通常のパラメータ取得後に、CSV分解に戻します。
    1040         *
    1041         * @param   clms データ変換を行うカラム列(カンマ区切り文字)
    1042         */
    1043        public void setAdjustColumns( final String clms ) {
    1044                adjustColumns = nval( getRequestParameter( clms ),adjustColumns );
    1045        }
    1046
    1047        /**
    1048         * 【TAG】NULL チェックすべきカラム列をカンマ区切り(CVS形式)で指定します。
    1049         *
    1050         * @og.tag nullCheck="AAA,BBB,CCC,DDD"
    1051         * 分解方法は、通常のパラメータ取得後に、CSV分解します。
    1052         *
    1053         * @og.rev 3.8.0.2 (2005/06/30) 新規追加
    1054         * @og.rev 3.8.8.5 (2007/03/09) 通常のパラメータ取得後に、CSV分解に戻します。
    1055         *
    1056         * @param   clms カラム列(CVS形式)
    1057         */
    1058        public void setNullCheck( final String clms ) {
    1059                nullCheck = nval( getRequestParameter( clms ),nullCheck );
    1060        }
    1061
    1062        /**
    1063         * 【TAG】行番号情報を、使用している/していない[true/false]を指定します(初期値:true)。
    1064         *
    1065         * @og.tag
    1066         * 通常のフォーマットでは、各行の先頭に行番号が出力されています。
    1067         * 読み取り時に、#NAME 属性を使用する場合は、この行番号を無視しています。
    1068         * #NAME 属性を使用せず、columns 属性でカラム名を指定する場合(他システムの
    1069         * 出力ファイルを読み取るケース等)では、行番号も存在しないケースがあり、
    1070         * その様な場合に、useNumber="false" を指定すれば、データの最初から読み取り始めます。
    1071         * この場合、出力データのカラムの並び順が変更された場合、columns 属性も
    1072         * 指定しなおす必要がありますので、できるだけ、#NAME 属性を使用するように
    1073         * してください。
    1074         * なお、EXCEL 入力には、この設定は適用されません。(暫定対応)
    1075         * 初期値は、true(使用する) です。
    1076         *
    1077         * @og.rev 3.7.0.5 (2005/04/11) 新規追加
    1078         *
    1079         * @param   useNo 行番号情報 [true:使用する/false:使用しない]
    1080         */
    1081        public void setUseNumber( final String useNo ) {
    1082                useNumber = nval( getRequestParameter( useNo ),useNumber );
    1083        }
    1084
    1085        /**
    1086         * 【TAG】読込件数が０件のとき処理を続行するかどうか[true/false]を指定します(初期値:false[続行する])。
    1087         *
    1088         * @og.tag
    1089         * 初期値は、false(続行する)です。
    1090         *
    1091         * @og.rev 4.3.7.0 (2009/06/01) 新規追加
    1092         *
    1093         * @param  cmd 読込件数が０件のとき、処理を [true:中止する/false:続行する]
    1094         */
    1095        public void setStopZero( final String cmd ) {
    1096                stopZero = nval( getRequestParameter( cmd ), stopZero );
    1097        }
    1098
    1099        /**
    1100         * 【TAG】(通常使いません)タグで処理される処理がメインとなるトランザクション処理かどうかを指定します(初期値:false)。
    1101         *
    1102         * @og.tag
    1103         * この値は、ファイルダウンロード処理に影響します。この値がtrueに指定された時にcommitされたDBTableModelが
    1104         * ファイルダウンロードの対象の表になります。
    1105         *
    1106         * このパラメーターは、通常、各タグにより実装され、ユーザーが指定する必要はありません。
    1107         * 但し、1つのJSP内でDBTableModelが複数生成される場合に、前に処理したDBTableModelについてファイルダウンロードをさせたい
    1108         * 場合は、後ろでDBTableModelを生成するタグで、明示的にこの値をfalseに指定することで、ファイルダウンロード処理の対象から
    1109         * 除外することができます。
    1110         *
    1111         * @og.rev 5.1.6.0 (2010/05/01) 新規作成
    1112         *
    1113         * @param  flag メイントランザクションかどうか
    1114         */
    1115        public void setMainTrans( final String flag ) {
    1116                isMainTrans = nval( getRequestParameter( flag ),isMainTrans );
    1117        }
    1118
    1119        /**
    1120         * 【TAG】(通常は使いません)データの読み飛ばし件数を設定します。
    1121         *
    1122         * @og.tag
    1123         * TAB区切りテキストやEXCEL等のデータの読み始めの初期値を指定します。
    1124         * ファイルの先頭行が、０行としてカウントしますので、設定値は、読み飛ばす
    1125         * 件数になります。(１と指定すると、１件読み飛ばし、２行目から読み込みます。)
    1126         * 読み飛ばしは、コメント行などは、無視しますので、実際の行数分読み飛ばします。
    1127         * ＃NAME属性や、columns 属性は、有効です。
    1128         *
    1129         * @og.rev 5.1.6.0 (2010/05/01) 新規作成
    1130         *
    1131         * @param       count 読み始めの初期値
    1132         */
    1133        public void setSkipRowCount( final String count ) {
    1134                skipRowCount = nval( getRequestParameter( count ),skipRowCount );
    1135        }
    1136
    1137        /**
    1138         * 【TAG】読取処理でラベルをコードリソースに逆変換を行うかどうかを指定します
    1139         *              (初期値:USE_TABLE_READER_RENDERER[={@og.value org.opengion.hayabusa.common.SystemData#USE_TABLE_READER_RENDERER}])。
    1140         *
    1141         * @og.tag
    1142         * TableWriter_Renderer 系のクラスで出力した場合は、コードリソースがラベルで出力されます。
    1143         * そのファイルを読み取ると、当然、エラーになります。
    1144         * ここでは、コードリソースのカラムに対して、ラベルからコードを求める逆変換を行うことで、
    1145         * Renderer 系で出力したファイルを取り込むことができるようにします。
    1146         *
    1147         * ここでは、TableWriter 系と同様に、TableReader_Renderer 系のクラスを作るのではなく、
    1148         * 属性値のフラグで、制御します。
    1149         * 将来的には、TableWriter 系も廃止して、同様のフラグで制御するように変更する予定です。
    1150         * (初期値:システム定数のUSE_TABLE_READER_RENDERER[={@og.value org.opengion.hayabusa.common.SystemData#USE_TABLE_READER_RENDERER}])。
    1151         *
    1152         * @og.rev 5.2.1.0 (2010/10/01) 新規作成
    1153         *
    1154         * @param  flag コードリソースのラベル逆変換を行うかどうか
    1155         * @see         org.opengion.hayabusa.common.SystemData#USE_TABLE_READER_RENDERER
    1156         */
    1157        public void setUseRenderer( final String flag ) {
    1158                useRenderer = nval( getRequestParameter( flag ),useRenderer );
    1159        }
    1160
    1161        /**
    1162         * 【TAG】matchKeysをカンマ区切りで指定します。
    1163         *
    1164         * @og.tag
    1165         * ファイルから特定の行のみを読み取るためのmatchKeysを指定します。
    1166         * matchKeysで指定したカラムに対して、matchValsの正規表現でチェックします。
    1167         * この機能はTableReader_Defaultのみ有効です。
    1168         * 通常は指定する必要はありません。
    1169         *
    1170         *
    1171         * @og.rev 5.9.8.1 (2016/05/13) 新規作成
    1172         *
    1173         * @param   keys カラム列(カンマ区切り文字)
    1174         */
    1175        public void setMatchKeys( final String keys ) {
    1176                matchKeys = getCSVParameter( keys );
    1177
    1178                if( matchVals != null && matchKeys.length != matchVals.length ) {
    1179                        String errMsg = "matchKeys属性とmatchVals属性の個数が合いません。"
    1180                                                + HybsSystem.CR
    1181                                                + " matchKeys=[" + matchKeys.length + "]:KEYS="
    1182                                                + StringUtil.array2csv( matchKeys ) + HybsSystem.CR
    1183                                                + " matchVals=[" + matchVals.length + "]:VLAS="
    1184                                                + StringUtil.array2csv( matchVals ) + HybsSystem.CR ;
    1185                        throw new HybsSystemException( errMsg );
    1186                }
    1187        }
    1188
    1189        /**
    1190         * 【TAG】matchKeysをカンマ区切りで指定します。
    1191         *
    1192         * @og.tag
    1193         * ファイルから特定の行のみを読み取るためのmatchKeysを指定します。
    1194         * matchKeysで指定したカラムに対して、matchValsの正規表現でチェックします。
    1195         * この機能はTableReader_Defaultのみ有効です。
    1196         * 通常は指定する必要はありません。
    1197         *
    1198         *
    1199         * @og.rev 5.9.8.1 (2016/05/13) 新規作成
    1200         *
    1201         * @param   keys カラム列(カンマ区切り文字)
    1202         */
    1203        public void setMatchVals( final String keys ) {
    1204                matchVals = getCSVParameter( keys );
    1205
    1206                if( matchKeys != null && matchKeys.length != matchVals.length ) {
    1207                        String errMsg = "matchKeys属性とmatchVals属性の個数が合いません。"
    1208                                                + HybsSystem.CR
    1209                                                + " matchKeys=[" + matchKeys.length + "]:KEYS="
    1210                                                + StringUtil.array2csv( matchKeys ) + HybsSystem.CR
    1211                                                + " matchVals=[" + matchVals.length + "]:VLAS="
    1212                                                + StringUtil.array2csv( matchVals ) + HybsSystem.CR ;
    1213                        throw new HybsSystemException( errMsg );
    1214                }
    1215        }
    1216
    1217        /**
    1218         * シリアライズ用のカスタムシリアライズ書き込みメソッド
    1219         *
    1220         * @og.rev 4.0.0.0 (2006/09/31) 新規追加
    1221         * @serialData 一部のオブジェクトは、シリアライズされません。
    1222         *
    1223         * @param       strm    ObjectOutputStreamオブジェクト
    1224         * @throws IOException  入出力エラーが発生した場合
    1225         */
    1226        private void writeObject( final ObjectOutputStream strm ) throws IOException {
    1227                strm.defaultWriteObject();
    1228        }
    1229
    1230        /**
    1231         * シリアライズ用のカスタムシリアライズ読み込みメソッド
    1232         *
    1233         * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。
    1234         *
    1235         * @og.rev 4.0.0.0 (2006/09/31) 新規追加
    1236         * @serialData 一部のオブジェクトは、シリアライズされません。
    1237         *
    1238         * @param       strm    ObjectInputStreamオブジェクト
    1239         * @see #release2()
    1240         * @throws IOException  シリアライズに関する入出力エラーが発生した場合
    1241         * @throws ClassNotFoundException       クラスを見つけることができなかった場合
    1242         */
    1243        private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException {
    1244                strm.defaultReadObject();
    1245        }
    1246
    1247        /**
    1248         * このオブジェクトの文字列表現を返します。
    1249         * 基本的にデバッグ目的に使用します。
    1250         *
    1251         * @return このクラスの文字列表現
    1252         */
    1253        @Override
    1254        public String toString() {
    1255                return org.opengion.fukurou.util.ToString.title( this.getClass().getName() )
    1256                                .println( "VERSION"                     ,VERSION        )
    1257                                .println( "separator"           ,separator              )
    1258                                .println( "fileURL"             ,fileURL                )
    1259                                .println( "filename"            ,filename               )
    1260                                .println( "encode"                      ,encode                 )
    1261                                .println( "readerClass"         ,readerClass    )
    1262                                .println( "maxRowCount"         ,maxRowCount    )
    1263                                .println( "displayMsg"          ,displayMsg             )
    1264                                .println( "executeCount"        ,executeCount   )
    1265                                .println( "modifyType"          ,modifyType             )
    1266                                .println( "checkColumns"        ,checkColumns   )
    1267                                .println( "adjustColumns"       ,adjustColumns  )
    1268                                .println( "nullCheck"           ,nullCheck              )
    1269                                .println( "command"                     ,command                )
    1270                                .println( "tableId"                     ,tableId                )
    1271                                .println( "sheetName"           ,sheetName              )
    1272                                .println( "sheetNos"            ,sheetNos               )               // 5.5.7.2 (2012/10/09)
    1273                                .println( "columns"                     ,columns                )
    1274                                .println( "useNumber"           ,useNumber              )
    1275                                .println( "Other..."    ,getAttributes().getAttribute() )
    1276                                .fixForm().toString() ;
    1277        }
    1278}