    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.process;
    017
    018import org.opengion.fukurou.util.Argument;
    019import org.opengion.fukurou.util.HybsEntry ;
    020import org.opengion.fukurou.xml.XSLT;
    021import org.opengion.fukurou.util.LogWriter;
    022
    023import java.util.Map ;
    024import java.util.LinkedHashMap ;
    025import java.io.File;
    026
    027/**
    028 * XSLT変換結果を指定のファイルに出力します。
    029 *
    030 * Process_XSLT は、AbstractProcess を継承した、ChainProcess インターフェース
    031 * の実装クラスです。
    032 * 上流(プロセスチェインのデータは上流から渡されます。)からのLineModel の
    033 * ファイルオブジェクトに対して、指定の XSL ファイルを適用して、XSL変換を行います。
    034 * 出力結果は、ファイル、または 標準出力に出力できます。
    035 *
    036 * 上流プロセスでは、Name 属性として、『File』を持ち、値は、Fileオブジェクト
    037 * である、Process_FileSearch を使用するのが、便利です。それ以外のクラスを
    038 * 使用する場合でも、Name属性と、File オブジェクトを持つ LineModel を受け渡し
    039 * できれば、使用可能です。
    040 *
    041 * -param_XXXX=固定値 を使用して、XSLTにパラメータを設定できます。
    042 *
    043 * それ以外では、org.opengion.fukurou.xml.XSLT で、入力ファイル情報の設定が可能に
    044 * なっている為、内部情報を使用するかどうか -useFileInfo を指定できます。
    045 * -useFileInfo=true とセットすると、以下の４項目が内部的にセットされます。
    046 *
    047 * 入力ファイル(inXMLのフルパス)     : FILEPATH  (例: G:\webapps\gf\jsp\DOC10\query.jsp)
    048 * 入力親フォルダ(inXMLの親フォルダ) : ADDRESS   (例: DOC10)
    049 * 入力ファイル(inXMLのファイル名)   : FILENAME  (例: query.jsp)
    050 * 入力ファイル(inXMLの更新日付  )   : MODIFIED  (例: yyyyMMddHHmmss形式)
    051 *
    052 * xsl ファイルでは、xsl:param で宣言し、xsl:value-of で取り出します。
    053 * &lt;xsl:param name="ADDRESS" select="" /&gt; と宣言しておき、必要な箇所で
    054 * &lt;xsl:value-of select="$ADDRESS"     /&gt; とすれば、取得できます。
    055 *
    056 * 引数文字列中にスペースを含む場合は、ダブルコーテーション("") で括って下さい。
    057 * 引数文字列の 『=』の前後には、スペースは挟めません。必ず、-key=value の様に
    058 * 繋げてください。
    059 *
    060 * @og.formSample
    061 *  Process_XSLT -xslfile=xslファイル -outfile=OUTFILE -append=true
    062 *
    063 *    -xslfile=xslファイル        ：変換を行う XSLファイル
    064 *   [-outfile=出力ファイル名   ] ：変換結果の出力ファイル名
    065 *   [-append=[false/true]      ] ：出力ファイルを、追記する(true)か新規作成する(false)か
    066 *   [-useFileInfo=[false/true] ] ：入力ファイル情報を、XSLTのパラメータにセットする(true)かしないか(false)か
    067 *   [-addROWSET=テーブル名     ] ：ヘッダー/フッターに ROWSET を追記します。
    068 *   [-headerXX=ヘッダー文字列  ] ：出力ファイルに、ヘッダー文字列を追記します。
    069 *                                         添え字(XX)が異なれば複数のヘッダーが指定できます。
    070 *   [-footerXX=フッター文字列  ] ：出力ファイルに、フッター文字列を追記します。
    071 *                                         添え字(XX)が異なれば複数のフッターが指定できます。
    072 *   [-param_XXXX=固定値        ] ：-param_SYSTEM_ID=GE
    073 *                                    XSLパーサーに対して、paramater を設定します。
    074 *                                    キーが異なれば、複数のパラメータを指定できます。
    075 *   [ -errAbend=[true/false]   ] ：異常発生時に、処理を中断(true)するか、継続(false)するかを指定する(初期値:true[中断する])
    076 *   [ -errXmlIn=[false/true]   ] ：異常発生時に、出力ファイルに、XML形式でエラーを追記するかを指定する(初期値:false[使用しない])
    077 *   [ -jspInclude=[true/false] ] ：jsp:directive.include 発見時に、そのファイルを INCLUDE するかを指定する(初期値:true[使用する])
    078 *   [ -realPath=実際の実行環境 ] ：jspInclude="true" 時に、/jsp/common/以下のファイルの取得先を指定します(初期値:null)
    079 *   [ -display=[false/true]    ] ：結果を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない])
    080 *   [ -debug=[false/true]      ] ：デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない])
    081 *
    082 * @version  4.0
    083 * @author   Kazuhiko Hasegawa
    084 * @since    JDK5.0,
    085 */
    086public class Process_XSLT extends AbstractProcess implements ChainProcess {
    087        private static final String PARAM_KEY   = "param_" ;
    088        private static final String HEADER_KEY  = "header" ;
    089        private static final String FOOTER_KEY  = "footer" ;
    090        private static final String FILE_KEY    = "File";
    091
    092        private static final String HEADER_XML    = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" ;
    093        private static final String HEADER_ROWSET = "<ROWSET tableName=\"TABLENAME\">" ;
    094        private static final String FOOTER_ROWSET = "</ROWSET>" ;
    095
    096        private XSLT            xslt            = null ;
    097        private HybsEntry[]     footerEntry     = null;
    098        private String          xslfile         = null ;
    099        private String          outfile         = null ;
    100        private boolean         errAbend        = true;         // 中断する
    101        private boolean         errXmlIn        = false;        // エラーXML形式
    102        private boolean         jspInclude      = true;         // 4.2.3.0 (2008/05/26)
    103        private String          realPath        = null ;        // 5.7.6.2 (2014/05/16) 追加
    104        private boolean         display         = false;        // 表示しない
    105        private boolean         debug           = false;        // 5.7.3.0 (2014/02/07) デバッグ情報
    106
    107        private int             clmNo           = -1;
    108        private int             inCount         = 0;
    109        private int             errCount        = 0;
    110        private String  tableName       = null;
    111
    112        private static final Map<String,String> mustProparty   ;          // ［プロパティ］必須チェック用 Map
    113        private static final Map<String,String> usableProparty ;          // ［プロパティ］整合性チェック Map
    114
    115        static {
    116                mustProparty = new LinkedHashMap<String,String>();
    117                mustProparty.put( "xslfile",    "変換を行う XSLファイル(必須)" );
    118
    119                usableProparty = new LinkedHashMap<String,String>();
    120                usableProparty.put( "outfile",          "変換結果の出力ファイル名" );
    121                usableProparty.put( "append",           "出力ファイルを、追記する(true)か新規作成する(false)か" );
    122                usableProparty.put( "useFileInfo",      "入力ファイル情報を、XSLTのパラメータにセットする(true)かしないか(false)か" );
    123                usableProparty.put( "addROWSET" ,       "ヘッダー/フッターに ROWSET を追記します。");
    124                usableProparty.put( "header",           "出力ファイルに、ヘッダー文字列を追記します。" +
    125                                                                        CR + "添え字(XX)が異なれば複数のヘッダーが指定できます。" );
    126                usableProparty.put( "footer",           "出力ファイルに、フッター文字列を追記します。" +
    127                                                                        CR + "添え字(XX)が異なれば複数のヘッダーが指定できます。" );
    128                usableProparty.put( "param_",           "XSLパーサーに対して、paramater を設定します。" +
    129                                                                        CR + "キーが異なれば、複数のパラメータを指定できます。" +
    130                                                                        CR + "例: -param_SYSTEM_ID=GE" );
    131                usableProparty.put( "errAbend", "異常発生時に、処理を中断(true)するか、継続(false)するか" +
    132                                                                        CR + "(初期値:true:中断する)" );
    133                usableProparty.put( "errXmlIn", "異常発生時に、出力ファイルに、XML形式でエラーを追記するかを指定する" +
    134                                                                        CR + "(初期値:false:使用しない)" );
    135                usableProparty.put( "jspInclude","jsp:directive.include 発見時に、そのファイルを INCLUDE するかを指定する" +
    136                                                                        CR + "(初期値:true:使用する)" );
    137                usableProparty.put( "realPath","jspInclude=\"true\" 時に、/jsp/common/以下のファイルの取得先を指定します。" +
    138                                                                        CR + "(初期値:null)" );            // 5.7.6.2 (2014/05/16) 追加
    139                usableProparty.put( "display",  "結果を標準出力に表示する(true)かしない(false)か" +
    140                                                                        CR + "(初期値:false:表示しない)" );
    141                usableProparty.put( "debug",    "デバッグ情報を標準出力に表示する(true)かしない(false)か" +
    142                                                                        CR + "(初期値:false:表示しない)" );             // 5.7.3.0 (2014/02/07) デバッグ情報
    143        }
    144
    145        /**
    146         * デフォルトコンストラクター。
    147         * このクラスは、動的作成されます。デフォルトコンストラクターで、
    148         * super クラスに対して、必要な初期化を行っておきます。
    149         *
    150         */
    151        public Process_XSLT() {
    152                super( "org.opengion.fukurou.process.Process_XSLT",mustProparty,usableProparty );
    153        }
    154
    155        /**
    156         * プロセスの初期化を行います。初めに一度だけ、呼び出されます。
    157         * 初期処理(ファイルオープン、ＤＢオープン等)に使用します。
    158         *
    159         * @og.rev 4.2.3.0 (2008/05/26) jsp:directive.include 処理の実施可否を引数指定します。
    160         * @og.rev 5.7.6.2 (2014/05/16) realPath 引数を追加します。
    161         *
    162         * @param   paramProcess データベースの接続先情報などを持っているオブジェクト
    163         */
    164        public void init( final ParamProcess paramProcess ) {
    165                Argument arg = getArgument();
    166
    167                xslfile                         = arg.getProparty( "xslfile" );
    168                outfile                         = arg.getProparty( "outfile" );
    169                tableName                       = arg.getProparty( "addROWSET" );
    170                boolean isAppend        = arg.getProparty( "append",false );
    171                boolean useFileInfo     = arg.getProparty( "useFileInfo",false );
    172                HybsEntry[] paramEntry  = arg.getEntrys( PARAM_KEY );           // 配列
    173                HybsEntry[] headerEntry = arg.getEntrys( HEADER_KEY );          // 配列
    174                footerEntry                     = arg.getEntrys( FOOTER_KEY );                  // 配列
    175                errAbend                        = arg.getProparty("errAbend",errAbend);
    176                errXmlIn                        = arg.getProparty("errXmlIn",errXmlIn);
    177                jspInclude                      = arg.getProparty("jspInclude",jspInclude);             // 4.2.3.0 (2008/05/26) 追加
    178                realPath                        = arg.getProparty("realPath"  ,realPath);               // 5.7.6.2 (2014/05/16) 追加
    179                display                         = arg.getProparty("display",display);
    180                debug                           = arg.getProparty("debug",debug);                               // 5.7.3.0 (2014/02/07) デバッグ情報
    181
    182                if( outfile != null ) {
    183                        File file = new File( outfile );                // 5.5.2.6 (2012/05/25) findbugs対応
    184                        File dir = file.getParentFile() ;
    185
    186                        // 親ディレクトリを示さない場合は null 。ディレクトリが存在しない、かつ、ディレクトリが作成できない場合の処理
    187                        if( dir != null && ! dir.exists() && ! dir.mkdirs() ) {
    188                                String errMsg = "ディレクトリが作成できませんでした。[" + dir + "]" ;
    189                                throw new RuntimeException( errMsg );
    190                        }
    191                }
    192                else {
    193                        // 出力先ファイル名が、指定されていない場合
    194                        String errMsg = "outfile が指定されていません。";
    195                        throw new RuntimeException( errMsg );
    196                }
    197
    198                xslt = new XSLT();
    199
    200                xslt.setOutFile( outfile,isAppend );
    201                xslt.setXslFile( xslfile );
    202                xslt.setParamEntry( paramEntry );
    203                xslt.useFileInfo( useFileInfo );
    204                xslt.errClose( errAbend );                      // エラー時に出力ファイルを閉じるかどうか。
    205                xslt.useErrXmlIn( errXmlIn );           // エラー時にXML形式で出力ファイルに追記するかどうか。
    206                xslt.jspInclude( jspInclude );          // 4.2.3.0 (2008/05/26) jsp:directive.include するかどうか
    207                xslt.setRealPath( realPath );           // 5.7.6.2 (2014/05/16) realPath 引数を追加します。
    208
    209                if( tableName != null ) {
    210                        xslt.setOutData( HEADER_XML );
    211                        xslt.setOutData( HEADER_ROWSET.replace( "TABLENAME",tableName ) );
    212                }
    213
    214                int size   = headerEntry.length;
    215                for( int i=0; i<size; i++ ) {
    216                        xslt.setOutData( headerEntry[i].getValue() );
    217                }
    218        }
    219
    220        /**
    221         * 引数の LineModel を処理するメソッドです。
    222         * 変換処理後の LineModel を返します。
    223         * 後続処理を行わない場合(データのフィルタリングを行う場合)は、
    224         * null データを返します。つまり、null データは、後続処理を行わない
    225         * フラグの代わりにも使用しています。
    226         * なお、変換処理後の LineModel と、オリジナルの LineModel が、
    227         * 同一か、コピー(クローン)かは、各処理メソッド内で決めています。
    228         * ドキュメントに明記されていない場合は、副作用が問題になる場合は、
    229         * 各処理ごとに自分でコピー(クローン)して下さい。
    230         *
    231         * @param   data        オリジナルのLineModel
    232         *
    233         * @return      処理変換後のLineModel
    234         */
    235        public LineModel action( final LineModel data ) {
    236                inCount++ ;
    237                if( display ) { println( data.dataLine() ); }
    238                if( clmNo < 0 ) { clmNo = data.getColumnNo( FILE_KEY ); }
    239                File file = (File)data.getValue( clmNo );
    240
    241                if( ! file.isFile() ) { return data; }
    242
    243                String filePath = file.getPath();
    244
    245                try {
    246                        if( debug ) { println( filePath ); }                    // 5.7.3.0 (2014/02/07) デバッグ情報
    247                        xslt.transform( filePath );
    248                }
    249                catch( RuntimeException ex ) {
    250                        errCount++ ;
    251                        if( errAbend ) { throw ex; }
    252                        else {
    253                                logging( ex.getMessage() );
    254                                logging( "xslfile  = " + xslfile );
    255                                logging( "outfile  = " + outfile );
    256                                logging( "xmlFile  = " + filePath );
    257                        }
    258                }
    259
    260                return data ;
    261        }
    262
    263        /**
    264         * プロセスの終了を行います。最後に一度だけ、呼び出されます。
    265         * 終了処理(ファイルクローズ、ＤＢクローズ等)に使用します。
    266         *
    267         * @param   isOK トータルで、OKだったかどうか[true:成功/false:失敗]
    268         */
    269        public void end( final boolean isOK ) {
    270                if( xslt != null ) {
    271                        if( isOK ) {
    272                                int size = footerEntry.length;
    273                                for( int i=0; i<size; i++ ) {
    274                                        xslt.setOutData( footerEntry[i].getValue() );
    275                                }
    276                                if( tableName != null ) {
    277                                        xslt.setOutData( FOOTER_ROWSET );
    278                                }
    279                        }
    280                        xslt.close();
    281                }
    282        }
    283
    284        /**
    285         * プロセスの処理結果のレポート表現を返します。
    286         * 処理プログラム名、入力件数、出力件数などの情報です。
    287         * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような
    288         * 形式で出してください。
    289         *
    290         * @return   処理結果のレポート
    291         */
    292        public String report() {
    293                String report = "[" + getClass().getName() + "]" + CR
    294                                + TAB + "XSL File   : " + xslfile   + CR
    295                                + TAB + "OUT File   : " + outfile   + CR
    296                                + TAB + "Table Name : " + tableName + CR
    297                                + TAB + "File Count : " + inCount   + CR
    298                                + TAB + "Err  Count : " + errCount ;
    299
    300                return report ;
    301        }
    302
    303        /**
    304         * このクラスの使用方法を返します。
    305         *
    306         * @return      このクラスの使用方法
    307         */
    308        public String usage() {
    309                StringBuilder buf = new StringBuilder();
    310
    311                buf.append( "XSLT変換結果を指定のファイルに出力します。"                                                                   ).append( CR );
    312                buf.append( CR );
    313                buf.append( "Process_XSLT は、AbstractProcess を継承した、ChainProcess インターフェース"        ).append( CR );
    314                buf.append( "の実装クラスです。"                                                                                                                 ).append( CR );
    315                buf.append( "上流(プロセスチェインのデータは上流から渡されます。)からのLineModel の"         ).append( CR );
    316                buf.append( "ファイルオブジェクトに対して、指定の XSL ファイルを適用して、XSL変換を"           ).append( CR );
    317                buf.append( "行います。出力結果は、ファイル、または 標準出力に出力できます。"                          ).append( CR );
    318                buf.append( CR );
    319                buf.append( "上流プロセスでは、Name 属性として、『File』を持ち、値は、Fileオブジェクト"               ).append( CR );
    320                buf.append( "である、Process_FileSearch を使用するのが、便利です。それ以外のクラスを"             ).append( CR );
    321                buf.append( "使用する場合でも、Name属性と、File オブジェクトを持つ LineModel を受け渡し"   ).append( CR );
    322                buf.append( "できれば、使用可能です。"                                                                                                              ).append( CR );
    323                buf.append( CR );
    324                buf.append( "-param_XXXX=固定値 を使用して、XSLTにパラメータを設定できます。"                          ).append( CR );
    325                buf.append( CR );
    326                buf.append( "それ以外では、org.opengion.fukurou.xml.XSLT で、入力ファイル情報の設定が可能に"            ).append( CR );
    327                buf.append( "なっている為、内部情報を使用するかどうか -useFileInfo を指定できます。"                ).append( CR );
    328                buf.append( "-useFileInfo=true とセットすると、以下の４項目が内部的にセットされます。"             ).append( CR );
    329                buf.append( CR );
    330                buf.append( "入力ファイル(inXMLのフルパス)     : FILEPATH  (例: G:/temp/DOC10/query.jsp)"   ).append( CR );
    331                buf.append( "入力親フォルダ(inXMLの親フォルダ) : ADDRESS   (例: DOC10)"                                               ).append( CR );
    332                buf.append( "入力ファイル(inXMLのファイル名)   : FILENAME  (例: query.jsp)"                                  ).append( CR );
    333                buf.append( "入力ファイル(inXMLの更新日付  )   : MODIFIED  (例: yyyyMMddHHmmss形式)"          ).append( CR );
    334                buf.append( CR );
    335                buf.append( "xsl ファイルでは、xsl:param で宣言し、xsl:value-of で取り出します。"                   ).append( CR );
    336                buf.append( "<xsl:param name=\"ADDRESS\" select=\"\" /> と宣言しておき、必要な箇所で"           ).append( CR );
    337                buf.append( "<xsl:value-of select=\"$ADDRESS\"     /> とすれば、取得できます。"                               ).append( CR );
    338                buf.append( CR );
    339                buf.append( "引数文字列中に空白を含む場合は、ダブルコーテーション(\"\") で括って下さい。" ).append( CR );
    340                buf.append( "引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に"                ).append( CR );
    341                buf.append( "繋げてください。"                                                                                                                          ).append( CR );
    342                buf.append( CR ).append( CR );
    343
    344                buf.append( getArgument().usage() ).append( CR );
    345
    346                return buf.toString();
    347        }
    348
    349        /**
    350         * このクラスは、main メソッドから実行できません。
    351         *
    352         * @param       args    コマンド引数配列
    353         */
    354        public static void main( final String[] args ) {
    355                LogWriter.log( new Process_XSLT().usage() );
    356        }
    357}