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.xml;
017
018import java.io.Reader;
019import java.io.Writer;
020import java.io.File;
021import java.io.IOException;
022import java.io.StringReader;
023
024import javax.xml.transform.TransformerException;
025import javax.xml.transform.TransformerConfigurationException;
026import javax.xml.transform.TransformerFactory;
027import javax.xml.transform.Transformer;
028import javax.xml.transform.stream.StreamSource;
029import javax.xml.transform.stream.StreamResult;
030import javax.xml.transform.ErrorListener;                                                       // 6.4.0.2 (2015/12/11)
031
032import org.opengion.fukurou.system.OgRuntimeException ;                         // 6.4.2.0 (2016/01/29)
033import org.opengion.fukurou.system.Closer ;
034import org.opengion.fukurou.system.DateSet;                                                     // 6.4.2.0 (2016/01/29)
035import org.opengion.fukurou.system.LogWriter;
036import org.opengion.fukurou.util.HybsEntry ;
037import org.opengion.fukurou.util.FileUtil ;
038import org.opengion.fukurou.util.StringUtil ;
039import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
040import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
041
042/**
043 * XML 入力ファイルに、XSL 入力ファイルを適用して、XSLT変換を行います。
044 * 結果は、XML 出力ファイルにセーブします。
045 * 各ファイルの代わりに、Writer,Reader を設定することも可能です。
046 *
047 * このパーサーでは、内部で実行中の入力ファイル情報を パラメータとして設定できます。
048 * useFileInfo( true ) とセットすると、以下の4項目が内部的にセットされます。
049 * ただし、この設定が可能なのは、XML 入力ファイルに、Reader ではなく、ファイル名を
050 * 渡した場合のみです。ストリームの場合は、各種情報は取れません。
051 *
052 * 入力ファイル(inXMLのフルパス)     : FILEPATH  (例: G:\webapps\gf\jsp\DOC10\query.jsp)
053 * 入力親フォルダ(inXMLの親フォルダ) : ADDRESS   (例: DOC10)
054 * 入力ファイル(inXMLのファイル名)   : FILENAME  (例: query.jsp)
055 * 入力ファイル(inXMLの更新日付  )   : MODIFIED  (例: yyyyMMddHHmmss形式)
056 *
057 * xsl ファイルでは、パラメータ は、xsl:param で宣言し、xsl:value-of で取り出します。
058 * <xsl:param name="ADDRESS" select="" /> と宣言しておき、必要な箇所で
059 * <xsl:value-of select="$ADDRESS"     /> とすれば、取得できます。
060 *
061 *      String inXSTL  = "inXSLfile.xsl" ;   // 入力XSLファイル
062 *      String outFile = "outXMLfile.xml" ;  // 出力XMLファイル
063 *      String inXML   = "inXMLfile.xml" ;   // 入力XMLファイル
064 *
065 *      XSLT xslt = new XSLT();
066 *      xslt.setXslFile( inXSTL );
067 *      xslt.setOutFile( outFile,false );
068 *
069 *      xslt.transform( inXML );
070 *
071 * @version  4.0
072 * @author   Kazuhiko Hasegawa
073 * @since    JDK5.0,
074 */
075public class XSLT {
076        /** 初期 ENCODE 名 {@value}        */
077        public static final String ENCODE = "UTF-8" ;
078
079        private Transformer transformer ;
080
081        private String          encode          = ENCODE;
082        private String          xmlFile         ;
083        private String          xslFile         ;
084        private String          outFile         ;
085        private Reader          xslReader       ;
086        private Writer          outWriter       ;
087        private HybsEntry[] paramEntry  ;
088        private boolean         isFileInfo      ;
089        private boolean         isErrClose      = true;
090        private boolean         isErrXmlIn      ;                       // useErrXmlIn ⇒ isErrXmlIn 変更
091        private boolean         isInclude       = true;         // 4.2.3.0 (2008/05/26)
092        private StreamResult result             ;
093
094        private String          realPath        ;                       // 5.7.6.2 (2014/05/16) 新規追加
095        private String          debugMsg        ;                       // 5.6.7.1 (2013/08/09) デバッグ用
096
097        /**
098         * デフォルトコンストラクター
099         *
100         * @og.rev 8.5.3.2 (2023/10/13) JDK21対応。警告: デフォルトのコンストラクタの使用で、コメントが指定されていません
101         */
102        public XSLT() { super(); }              // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
103
104        /**
105         * 入力XSLファイルを、指定します。
106         *
107         * @param       file    入力XSLファイル
108         * @see #setXslFile( Reader )
109         */
110        public void setXslFile( final String file ) {
111                xslFile = file;
112                setXslFile( FileUtil.getBufferedReader( new File( xslFile ),encode ) );
113        }
114
115        /**
116         * 入力XSLリーダーを、指定します。
117         *
118         * @param       reader  入力XSLリーダー
119         * @see #setXslFile( String )
120         */
121        public void setXslFile( final Reader reader ) {
122                transformer = null;
123                xslReader   = reader;
124        }
125
126        /**
127         * 結果XML ファイル名と、そのオープン方法を指定します。
128         * 結果XML ファイルを、追記する(append=true)か新規作成する(append=false)か指定します。
129         * なお、結果XML ファイル(outFile) を指定しない(=null)か、特別な名称 "System.out"
130         * 文字列を渡すと、標準出力に 結果を出力します。
131         *
132         * @param       file    出力ファイル名(null または、"System.out" 文字列時は、標準出力)
133         * @param append [true]追記する/false:新規作成する]
134         */
135        public void setOutFile( final String file,final boolean append ) {
136                outFile = file ;
137                setOutFile( FileUtil.getPrintWriter( new File( outFile ),encode,append ) );
138        }
139
140        /**
141         * 結果XML データを出力する、Writer を指定します。
142         * ファイル、標準出力、JSPWriter など、必要に応じて Writer を作成してください。
143         * 標準出力(System.out)の場合は、NonClosePrintWriter クラスなどの非close()処理系を、
144         * JSPWriterの場合は、NonFlushPrintWriter クラスなどの非flush()、close()処理系を、
145         * 使用してください。
146         *
147         * @param       writer  出力するWriter
148         */
149        public void setOutFile( final Writer writer ) {
150                Closer.ioClose( outWriter );
151                outWriter = writer ;
152                result = new StreamResult( outWriter );
153        }
154
155        /**
156         * 結果XML ライターに、指定のデータを書き出します。
157         *
158         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
159         *
160         * @param       outData 書き出すデータ
161         */
162        public void setOutData( final String outData ) {
163                if( outData != null && outData.length() > 0 ) {
164                        // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
165                        if( outWriter == null ) {
166                                final String errMsg = "#setOutFile(Writer)を先に実行しておいてください。"      + CR
167                                                                + "   outData =" + outData      + CR;
168                                throw new OgRuntimeException( errMsg );
169                        }
170
171                        try {
172                                outWriter.write( outData );
173                                outWriter.write( CR );
174                        }
175                        catch( final IOException ex ) {
176                                final String errMsg = "ライターにデータ登録を失敗しました。" + CR
177                                                                + ex.getMessage() ;
178                                close();
179                                throw new OgRuntimeException( errMsg,ex );
180                        }
181                }
182        }
183
184        /**
185         * XML ファイルをXSLT変換します。
186         * XML 入力ファイルに、XSL 入力ファイルを適用して、XSLT変換を行います。
187         * 結果は、XML ファイルにセーブします。
188         * 拡張子が『.jsp』で、かつ、isInclude=true の場合、jsp:directive.include 処理を行います。
189         *
190         * @og.rev 4.2.3.0 (2008/05/26) jsp:directive.include 処理の実施可否を引数指定します。
191         * @og.rev 5.2.1.0 (2010/10/01) JspIncludeReader#getString の第3引数を廃止
192         * @og.rev 5.6.7.1 (2013/08/09) デバッグ用に、ファイルリストを取得しておきます。
193         * @og.rev 5.7.6.2 (2014/05/16) #transform( String , boolean ) 廃止。realPath 追加
194         *
195         * @param       file    入力XMLファイル
196         */
197        public void transform( final String file ) {
198                xmlFile = file;                                                                         // エラーメッセージ表示時に使用する。
199
200                if( xmlFile.endsWith( ".jsp" ) && isInclude ) {
201                        // 5.6.7.1 (2013/08/09) デバッグ用に、ファイルリストを取得しておきます。
202                        final JspIncludeReader incReader = new JspIncludeReader();
203                        incReader.setRealPath( realPath );                              // 5.7.6.2 (2014/05/16) realPath 追加
204                        debugMsg = incReader.getIncludeFiles();
205
206                        final String incData = incReader.getString( new File( xmlFile ),encode );                               // 5.2.1.0 (2010/10/01)
207                        transform( new StringReader( incData ) );
208                }
209                else {
210                        transform( FileUtil.getBufferedReader( new File( xmlFile ),encode ) );
211                }
212        }
213
214        /**
215         * XML ファイルをXSLT変換します。
216         * XML 入力リーダーに、XSL 入力リーダーを適用して、XSLT変換を行います。
217         * 結果は、XML ライターに書き出します。
218         * この処理の終了後に、入力XML リーダー は、close() されます。
219         *
220         * @og.rev 5.6.5.2 (2013/06/21) エラーメッセージが判りにくいので、追記します。
221         * @og.rev 5.6.7.1 (2013/08/09) デバッグ用に、ファイルリストを出力します。
222         * @og.rev 6.4.0.2 (2015/12/11) Transformer のエラーを、より詳細に出力します。
223         * @og.rev 8.5.4.2 (2024/01/12) エラー時の情報に 入力ファイル(xmlFile)があれば表示する。
224         *
225         * @param       xmlReader       入力XML リーダー
226         * @see #transform( String )
227         */
228        public void transform( final Reader xmlReader ) {
229                HybsEntry[] entry = null;
230
231                // transformer は使いまわしますが、ErrorListener は、Reader 毎に再作成します。
232                final ErrorListener errListener = new HybsErrorListener();                      // 6.4.0.2 (2015/12/11)
233                try {
234                        if( transformer == null ) {
235                                init( errListener );                                                                            // 6.4.0.2 (2015/12/11)
236                        }
237                        else {
238                                transformer.reset();
239                                transformer.setErrorListener( errListener );                            // 6.4.0.2 (2015/12/11)
240                        }
241
242                        // 入力XMLファイルのファイル情報を設定します。
243                        if( isFileInfo && xmlFile != null ) {
244                                entry = getXmlParameter( xmlFile );
245                                parameterSet( transformer,entry );
246                        }
247                        // 8.5.4.2 (2024/01/12) エラー時の情報に使用するので、クリアは、final で行う。
248//                      xmlFile = null ;
249
250                        // 入力XMLリーダーからStreamSourceを作る
251                        final StreamSource data = new StreamSource( xmlReader );
252                        transformer.transform( data,result );
253                }
254                catch( final TransformerException ex ) {
255                        // 5.7.3.0 (2014/02/07) エラー情報をもう少し詳細に取得します。
256        //              final String errMsg = ex.getMessageAndLocation() ;
257                        final String errMsg = errListener.toString();                                   // 6.4.0.2 (2015/12/11) さらに詳細に出します。
258
259                        final StringBuilder errBuf = new StringBuilder( BUFFER_MIDDLE )
260                                .append( "=====================================================" ).append( CR )
261                                .append( "XML-XSLT 変換に失敗しました。" ).append( CR )
262                                // 8.5.4.2 (2024/01/12) エラー時の情報に 入力ファイル(xmlFile)があれば表示する。
263//                              .append( errMsg );
264                                .append( errMsg ).append( CR )
265                                .append( " [xmlFile = " ).append( xmlFile ).append( ']' );
266
267                        // 6.3.1.0 (2015/06/28) デバッグ用メッセージを出力します。
268                        if( debugMsg != null && debugMsg.length() > 0 ) {
269                                errBuf.append( CR ).append( debugMsg );
270                        }
271
272                        // 5.6.5.2 (2013/06/21) エラーメッセージが判りにくいので、追記します。
273                        if( errMsg.indexOf( "プロローグにはコンテンツを指定できません" ) >= 0 ) {
274                                errBuf.append( CR ).append( "\t(UTF-8変換時に、BOMが付くとこのエラーが出ます。BOMを外してみてください。)" );
275                        }
276
277                        // 5.6.7.1 (2013/08/09) デバッグ用に、ファイルリストを出力します。
278                        if( errMsg.indexOf( "で終了する必要があります" ) >= 0 ) {
279                                errBuf.append( CR ).append( "\t(不整合は、includeファイルの可能性があります。)" );
280                        }
281                        errBuf.append( CR );
282
283                        if( isErrXmlIn ) { setOutData( toXmlRow( entry, ex ) ); }
284
285                        if( isErrClose ) { close(); }
286
287                        throw new OgRuntimeException( errBuf.toString(),ex );
288                }
289                finally {
290                        Closer.ioClose( xmlReader );
291                        xmlFile = null ;                                // 8.5.4.2 (2024/01/12)
292                }
293        }
294
295        /**
296         * Transformer オブジェクトに対して、Parameter を設定します。
297         *
298         * 指定されたパラメーターキーは、xsl ファイルでは、xsl:param で宣言し、
299         * xsl:value-of で取り出します。
300         * <xsl:param name="ADDRESS" select="" /> と宣言しておき、必要な箇所で
301         * <xsl:value-of select="$ADDRESS"     /> とすれば、取得できます。
302         *
303         * @param       entry   HybsEntry配列(可変長引数)
304         */
305        public void setParamEntry( final HybsEntry... entry ) {
306                if( entry != null && entry.length > 0 ) {               // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
307                        paramEntry = new HybsEntry[entry.length];
308                        System.arraycopy( entry,0,paramEntry,0,entry.length );
309                }
310        }
311
312        /**
313         * transform 処理中にエラーが発生した場合に、出力ファイルを閉じるかどうかを指定します。
314         *
315         * 処理途中でエラーが発生した場合に、そこで処理を中断するか、それとも、
316         * 無視して、さらに処理を進めるかを指定することが可能です。
317         * 継続して処理を進めたい場合は、出力ファイルを閉じないため、false を
318         * 設定します。ただし、エラー時には、RuntimeException は throw されます。
319         * 初期値は、true(閉じる)です。
320         *
321         * @param       flag    エラー時クローズ [true:閉じる/false:閉じない]
322         */
323        public void errClose( final boolean flag ) {
324                isErrClose = flag ;
325        }
326
327        /**
328         * transform 処理中エラーを、出力ファイルに、XML形式でエラーを追記するかどうかを指定します。
329         *
330         * 処理途中でエラーが発生した場合に、ログだけではなく、結果XMLファイルに、
331         * エラー内容や、エラーファイルなどを埋め込むと、XMLファイルとしてDB登録や、
332         * その他集計等に使えます。
333         * 今は、GE70 スキーマ形式のファイルしか作成できません。
334         * これは、#errClose( boolean ) メソッドと共に使用すると効果的です。
335         * つまり、errClose = false; にして、エラー時でも出力ファイルを閉じずに、
336         * 処理を続ける事で、エラーメッセージもXMLファイルとして蓄積できます。
337         * 初期値は、false(使用しない)です。
338         *
339         * @param       flag    エラー時XML形式 [false:使用しない/true:使用する]
340         */
341        public void useErrXmlIn( final boolean flag ) {
342                isErrXmlIn = flag ;
343        }
344
345        /**
346         * jsp:directive.include 発見時に、そのファイルを INCLUDE するかを指定するかどうかを指定します(初期値:true:使用する)
347         *
348         * 引数の処理対象ファイル(transformの引数ファイル)が、『.jsp』の場合、
349         * jsp:directive.include 発見時に、そのファイルを INCLUDE するかを指定するか
350         * どうかを指定します。
351         * インクルードされたファイルとあわせて、正規のXML にならないと、パーサー
352         * エラーが発生します。
353         * JSPソース解析を行うには、INCLUDE ファイルも考慮しないと正確な結果を
354         * 得られませんが、INCLUDE 先のファイルまで合わせる必要があるため、
355         * 場合によっては、INCLUDEファイルを無視しなければならないケースがあります。
356         * 初期値は、true(使用する)です。
357         *
358         * @param       flag    エラー時XML形式 [false:使用しない/true:使用する]
359         */
360        public void jspInclude( final boolean flag ) {
361                isInclude = flag ;
362        }
363
364        /**
365         * jspInclude=true 時に、/jsp/common/** 等の include ファイルが存在しない場合の共有取得場所を指定します。
366         *
367         * 引数の処理対象ファイル(transformの引数ファイル)が、『.jsp』で、かつ、jspInclude=true の場合、
368         * そのファイルを INCLUDE するのですが、/jsp/common/** 等の include ファイルは、
369         * エンジン共通として、jspCommon6.x.x.x.jar で提供しています。
370         * 従来は、処理対象jspの相対パスで、../../../gf/jsp/commom/** を取り込んでいましたが、
371         * Tomcat起動フォルダ以外のシステムのJSPチェックなどを行う場合は、gf フォルダが存在しない
372         * ケースがあります。
373         * そこで、確実にgf が存在する、処理をキックしている環境の gf を使用するように変更します。
374         * その環境とは、つまり、エンジン内部変数の REAL_PATH ですが、jsp などが実行していないと取得できません。
375         *
376         * @param       path    /jsp/common/** 等の include ファイルの共有取得場所
377         */
378        public void setRealPath( final String path ) {
379                realPath = path ;
380        }
381
382        /**
383         * 入力XSLファイルのストリームを閉じます。
384         *
385         * @og.rev 5.6.7.1 (2013/08/09) includeしたファイルのキャッシュをクリアします。
386         */
387        public void close() {
388                Closer.ioClose( outWriter );
389
390                // 5.6.7.1 (2013/08/09) includeしたファイルのキャッシュをクリア
391                JspIncludeReader.cacheClear();
392        }
393
394        /**
395         * XML ファイルをXSLT変換します。
396         * XML 入力ファイルに、XSL 入力ファイルを適用して、XSLT変換を行います。
397         * 結果は、XML ファイルにセーブします。
398         * なお、結果XML ファイル(outFile) に、特別な名称 "System.out" 文字列を渡すと、
399         * 標準出力に 結果を出力します。
400         *
401         * @og.rev 5.6.7.1 (2013/08/09) includeしたファイルのキャッシュをクリアします。
402         * @og.rev 6.4.0.2 (2015/12/11) Transformer のエラーを、より詳細に出力します。
403         *
404         * @param       errListener ErrorListenerオブジェクト
405         */
406        private void init( final ErrorListener errListener ) {
407                try {
408                        // xsl属性からStreamSourceを作る
409                        final StreamSource style = new StreamSource( xslReader );
410
411                        // Transformerを作り、XMLを変換する
412                        final TransformerFactory tFactory = TransformerFactory.newInstance();
413
414                        // 6.4.0.2 (2015/12/11) Transformer のエラーを、より詳細に出力します。
415                        tFactory.setErrorListener( errListener );
416
417                        transformer = tFactory.newTransformer( style );
418                        // 6.4.0.2 (2015/12/11) Transformer のエラーを、より詳細に出力します。
419                        transformer.setErrorListener( errListener );
420
421                        parameterSet( transformer,paramEntry );
422
423                        // 5.6.7.1 (2013/08/09) includeしたファイルのキャッシュをクリア
424                        JspIncludeReader.cacheClear();
425                }
426                catch( final TransformerConfigurationException ex ) {
427                        final String errMsg = xslFile + "ファイルの XSLT 解析に失敗しました。" + CR
428        //                                                      + ex.getMessageAndLocation()    // 6.4.0.2 (2015/12/11) 行番号がうまく取り出せない。
429                                                                + errListener.toString();               // 6.4.0.2 (2015/12/11) エラー内容が重複するかも。
430                        throw new OgRuntimeException( errMsg,ex );
431                }
432                finally {
433                        Closer.ioClose( xslReader );
434                        xslReader = null;
435                }
436        }
437
438        /**
439         * 実行中の入力ファイル名などの属性情報を パラメータとして設定するかどうかを指定します。
440         *
441         * このパーサーでは、内部で実行中の入力ファイル情報を パラメータとして設定できます。
442         * useFileInfo( true ) とセットすると、以下の4項目が内部的にセットされます。
443         *
444         * 入力ファイル(inXMLのフルパス)     : FILEPATH  (例: G:\webapps\gf\jsp\DOC10\query.jsp)
445         * 入力親フォルダ(inXMLの親フォルダ) : ADDRESS   (例: DOC10)
446         * 入力ファイル(inXMLのファイル名)   : FILENAME  (例: query.jsp)
447         * 入力ファイル(inXMLの更新日付  )   : MODIFIED  (例: yyyyMMddHHmmss形式)
448         *
449         * @og.rev 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追加。
450         * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
451         *
452         * xsl ファイルでは、xsl:param で宣言し、xsl:value-of で取り出します。
453         * <xsl:param name="ADDRESS" select="" /> と宣言しておき、必要な箇所で
454         * <xsl:value-of select="$ADDRESS"     /> とすれば、取得できます。
455         *
456         * 初期値は、false(セットしない) です。
457         *
458         * @param       flag    セットする:true/セットしない:false
459         */
460        public void useFileInfo( final boolean flag ) {
461                isFileInfo = flag;
462        }
463
464        /**
465         * ファイル名指定で XML,XSL,OUTファイルを指定する場合のエンコードを指定します。
466         *
467         * 初期値は、UTF-8 です。
468         *
469         * @param       encode  エンコード
470         */
471        public void useEncode( final String encode ) {
472                this.encode = encode;
473        }
474
475        /**
476         * 実行中の入力ファイル名などの属性情報を パラメータとして取得します。
477         *
478         * 入力ファイル(inXMLのフルパス)     : FILEPATH  (例: G:\webapps\gf\jsp\DOC10\query.jsp)
479         * 入力ファイル(inXMLのファイル名)   : FILENAME  (例: query.jsp)
480         * 入力親フォルダ(inXMLの親フォルダ) : ADDRESS   (例: DOC10)
481         * 入力ファイル(inXMLの更新日付  )   : MODIFIED  (例: yyyyMMddHHmmss形式)
482         *
483         * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
484         *
485         * @param       xmlIn   XML入力ファイル
486         *
487         * @return      HybsEntry配列
488         */
489        private HybsEntry[] getXmlParameter( final String xmlIn ) {
490                final HybsEntry[] entry = new HybsEntry[4] ;            // 8.5.4.2 (2024/01/12) PMD 7.0.0 LocalVariableCouldBeFinal
491
492                entry[0] = new HybsEntry( "FILEPATH" , xmlIn) ;
493
494                final File xmlFile = new File( xmlIn );
495                entry[1] = new HybsEntry( "FILENAME" , xmlFile.getName()) ;
496
497                final File parentFile = xmlFile.getParentFile() ;
498                // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
499                entry[2] = parentFile == null
500                                                ? new HybsEntry( "ADDRESS" , "" )
501                                                : new HybsEntry( "ADDRESS" , parentFile.getName()) ;
502
503                final String lastDate = DateSet.getDate( xmlFile.lastModified() , "yyyyMMddHHmmss" ) ;          // 5.5.7.2 (2012/10/09) HybsDateUtil を利用
504                entry[3] = new HybsEntry( "MODIFIED" , lastDate ) ;
505
506                return entry ;
507        }
508
509        /**
510         * Transformer オブジェクト に、パラメータを設定します。
511         *
512         * 指定されたパラメーターキーは、xsl ファイルでは、xsl:param で宣言し、
513         * xsl:value-of で取り出します。
514         * <xsl:param name="ADDRESS" select="" /> と宣言しておき、必要な箇所で
515         * <xsl:value-of select="$ADDRESS"     /> とすれば、取得できます。
516         *
517         * @param former Transformerオブジェクト
518         * @param entry  パラメータ配列(可変長引数)
519         */
520        private void parameterSet( final Transformer former,final HybsEntry... entry ) {
521                if( entry != null && entry.length > 0 ) {               // 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
522                        final int size   = entry.length;
523                        for( int i=0; i<size; i++ ) {
524                                final String key = entry[i].getKey() ;
525                                final String val = entry[i].getValue();
526                                former.setParameter( key , val );
527                        }
528                }
529        }
530
531        /**
532         * このオブジェクトの内部文字列表現を返します。
533         *
534         * 接続URL + "," + 接続ユーザー + " (" + 作成日付 + ")" です。
535         *
536         * @return 内部文字列表現
537         * @og.rtnNotNull
538         */
539        @Override
540        public String toString() {
541                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
542                        .append( "XSL File:" ).append( xslFile ).append( CR )
543                        .append( "XML File:" ).append( xmlFile ).append( CR )
544                        .append( "OUT File:" ).append( outFile ).append( CR );
545
546                return buf.toString() ;
547        }
548
549        /**
550         * エラー情報の内部XML文字列表現を返します。
551         *
552         * エラー時の情報も、XML化して保存する為の簡易処理。
553         * ここでは、XMLスキーマは、固定で、GF70 の形式になります。
554         *
555         * @og.rev 4.2.3.0 (2008/05/26) エラー発生時のXMLファイルを追加します。
556         * @og.rev 5.2.1.0 (2010/10/01) XML形式を変更します。(TEXT⇒TEXT_DATA)
557         *
558         * @param       entry   HybsEntry配列
559         * @param       ex              エラー情報
560         *
561         * @return XMLの部分文字列
562         * @og.rtnNotNull
563         */
564        private String toXmlRow( final HybsEntry[] entry,final TransformerException ex ) {
565                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
566
567                // 6.0.2.5 (2014/10/31) char を append する。
568                buf.append( "<ROW>" ).append( CR );
569                if( paramEntry != null ) {
570                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach
571//                      for( int i=0; i<paramEntry.length; i++ ) {
572//                              final String key = paramEntry[i].getKey() ;
573//                              final String val = paramEntry[i].getValue();
574                        for( final HybsEntry prmEntry : paramEntry ) {
575                                final String key = prmEntry.getKey() ;
576                                final String val = prmEntry.getValue();
577                                buf.append( "  <" ).append( key ).append( '>' )
578                                        .append( val )
579                                        .append( "</" ).append( key ).append( '>' )
580                                        .append( CR );
581                        }
582                }
583
584                if( entry != null ) {
585                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach
586//                      for( int i=0; i<entry.length; i++ ) {
587//                              final String key = entry[i].getKey() ;
588//                              final String val = entry[i].getValue();
589                        for( final HybsEntry hent : entry ) {
590                                final String key = hent.getKey() ;
591                                final String val = hent.getValue();
592                                buf.append( "  <" ).append( key ).append( '>' )
593                                        .append( val )
594                                        .append( "</" ).append( key ).append( '>' )
595                                        .append( CR );
596                        }
597                }
598
599                buf.append( "  <TAGNAME />" ).append( CR )                                              // XML なので、このまま。
600                        .append( "  <MSGCD>XML_ERROR</MSGCD>" ).append( CR )
601                        .append( "  <MSGTXT>XML-XSLT 変換に失敗しました。</MSGTXT>" ).append( CR );
602
603                String errMsg = StringUtil.htmlFilter( ex.getMessage() );
604                final int indx = errMsg.lastIndexOf( "Exception:" );
605                if( indx >= 0 ) {
606                        errMsg = errMsg.substring( indx + "Exception:".length() );
607                }
608                buf.append( "  <TEXT_DATA>" ).append( errMsg ).append( CR )             // 5.2.1.0 (2010/10/01)
609                        .append( " Location:" ).append( ex.getLocationAsString() ).append( CR )
610                        .append( "</TEXT_DATA>" ).append( CR )          // 5.2.1.0 (2010/10/01)
611                        .append( "</ROW>" ).append( CR );
612
613                return buf.toString() ;
614
615/*
616           <ROW>
617                 <SYSTEM_ID>  </SYSTEM_ID>
618                 <ADDRESS  >  </ADDRESS>
619                 <FILENAME >  </FILENAME>
620                 <FILEPATH >  </FILEPATH>
621                 <MODIFIED >  </MODIFIED>
622                 <TAGNAME  >  </TAGNAME>
623                 <MSGCD    >  </MSGCD>
624                 <MSGTXT   >  </MSGTXT>
625                 <TEXT_DATA>  </TEXT_DATA>
626           </ROW>
627*/
628        }
629
630        /**
631         * テスト用のメインメソッド。
632         *
633         * java org.opengion.fukurou.xml.XSLT in_xml in_xsl out_xml
634         *
635         * @param       args    コマンド引数配列
636         * @throws IOException 入出力エラーが発生した場合
637         */
638        public static void main( final String[] args ) throws IOException {
639                if( args.length != 3 ) {
640                        LogWriter.log( "Usage: java org.opengion.fukurou.xml.XSLT in_xml in_xsl out_xml" );
641                        LogWriter.log( "  XML 入力ファイルに、XSL 入力ファイルを適用して、" );
642                        LogWriter.log( "  XSLT変換を行います。" );
643                        LogWriter.log( "  結果は、XML ファイルにセーブします。" );
644                        LogWriter.log( "  out_xml に System.out を指定すると標準出力に出力します。" );
645                        return ;
646                }
647
648                final XSLT xslt = new XSLT();
649                xslt.setXslFile( args[1] );
650                xslt.setOutFile( args[2],false );
651                xslt.transform( args[0] );
652                xslt.close();
653        }
654}