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 */
016 package org.opengion.fukurou.xml;
017
018 import org.opengion.fukurou.util.Closer ;
019 import org.opengion.fukurou.util.HybsEntry ;
020 import org.opengion.fukurou.util.FileUtil ;
021 import org.opengion.fukurou.util.StringUtil ;
022 import org.opengion.fukurou.util.LogWriter;
023 import org.opengion.fukurou.util.HybsDateUtil ;
024
025 import java.io.Reader;
026 import java.io.Writer;
027 import java.io.File;
028 import java.io.IOException;
029 import java.io.StringReader;
030 // import java.text.DateFormat;
031 // import java.text.SimpleDateFormat;
032 // import java.util.Date;
033 // import java.util.Locale;
034
035 import javax.xml.transform.TransformerException;
036 import javax.xml.transform.TransformerConfigurationException;
037 import javax.xml.transform.TransformerFactory;
038 import javax.xml.transform.Transformer;
039 import javax.xml.transform.stream.StreamSource;
040 import javax.xml.transform.stream.StreamResult;
041
042 /**
043 * XML 入力ファイルに、XSL 入力ファイルを適用して?XSLT変換を行います?
044 * 結果は、XML 出力ファイルにセーブします?
045 * ?ァイルの代わりに、Writer,Reader を設定することも可能です?
046 *
047 * こ?パ?サーでは、?部で実行中の入力ファイル???パラメータとして設定できます?
048 * useFileInfo( true ) とセ?すると、以下???目が?部?セ?されます?
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" ; // 入力X??ファイル
062 * String outFile = "outXMLfile.xml" ; // 出力X??ファイル
063 * String inXML = "inXMLfile.xml" ; // 入力X??ファイル
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 */
075 public class XSLT {
076 private static final String CR = System.getProperty("line.separator") ;
077 /** 初期 ENCODE ?{@value} */
078 public static final String ENCODE = "UTF-8" ;
079
080 private Transformer transformer = null;
081
082 private String encode = ENCODE;
083 private String xmlFile = null;
084 private String xslFile = null;
085 private String outFile = null;
086 private Reader xslReader = null;
087 private Writer outWriter = null;
088 private HybsEntry[] paramEntry = null;
089 private boolean isFileInfo = false;
090 private boolean isErrClose = true;
091 private boolean isErrXmlIn = false; // useErrXmlIn ?isErrXmlIn 変更
092 private boolean isInclude = true; // 4.2.3.0 (2008/05/26)
093 private StreamResult result = null;
094
095 // private DateFormat formatter = null; // HybsDateUtil を利用
096
097 private String jspIncludeData = null;
098
099 /**
100 * 入力XSLファイルを??します?
101 *
102 * @param file 入力XSLファイル
103 * @see #setXslFile( Reader )
104 */
105 public void setXslFile( final String file ) {
106 xslFile = file;
107 setXslFile( FileUtil.getBufferedReader( new File( xslFile ),encode ) );
108 }
109
110 /**
111 * 入力XSLリー??を??します?
112 *
113 * @param reader 入力XSLリー??
114 * @see #setXslFile( String )
115 */
116 public void setXslFile( final Reader reader ) {
117 transformer = null;
118 xslReader = reader;
119 }
120
121 /**
122 * 結果XML ファイル名と、そのオープン方法を?します?
123 * 結果XML ファイルを?追記す?append=true)か新規作?する(append=false)か指定します?
124 * なお?結果XML ファイル(outFile) を指定しな?=null)か?特別な名称 "System.out"
125 * ??を渡すと、標準?力に 結果を?力します?
126 *
127 * @param file 出力ファイル?null また??System.out" ??時?、標準??
128 * @param append [true]追記す?false:新規作?する]
129 */
130 public void setOutFile( final String file,final boolean append ) {
131 outFile = file ;
132 setOutFile( FileUtil.getPrintWriter( new File( outFile ),encode,append ) );
133 }
134
135 /**
136 * 結果XML ??タを?力する?Writer を指定します?
137 * ファイル、標準?力?JSPWriter など、?に応じて Writer を作?してください?
138 * 標準??System.out)の場合?、NonClosePrintWriter クラスなどの非close()処?を?
139 * JSPWriterの場合?、NonFlushPrintWriter クラスなどの非flush()、close()処?を?
140 * 使用してください?
141 *
142 * @param writer 出力するWriter
143 */
144 public void setOutFile( final Writer writer ) {
145 Closer.ioClose( outWriter );
146 outWriter = writer ;
147 result = new StreamResult( outWriter );
148 }
149
150 /**
151 * 結果XML ライターに、指定???タを書き?します?
152 *
153 * @param outData 書き?すデータ
154 */
155 public void setOutData( final String outData ) {
156 if( outData != null && outData.length() > 0 ) {
157 try {
158 outWriter.write( outData );
159 outWriter.write( CR );
160 }
161 catch( IOException ex ) {
162 String errMsg = "ライターに??タ登録を失敗しました? + CR
163 + ex.getMessage() ;
164 close();
165 throw new RuntimeException( errMsg,ex );
166 }
167 }
168 }
169
170 /**
171 * XML ファイルをXSLT変換します?
172 * XML 入力ファイルに、XSL 入力ファイルを適用して?XSLT変換を行います?
173 * 結果は、XML ファイルにセーブします?
174 * 拡張子が?jsp』?場合?jsp:directive.include 処?行います?
175 *
176 * @og.rev 4.0.0.2 (2007/12/10) 拡張子が?jsp』?場合?jsp:directive.include 処?行います?
177 *
178 * @param file 入力XMLファイル
179 * @see #transform( Reader )
180 */
181 public void transform( final String file ) {
182 transform( file, isInclude );
183 }
184
185 /**
186 * XML ファイルをXSLT変換します?
187 * XML 入力ファイルに、XSL 入力ファイルを適用して?XSLT変換を行います?
188 * 結果は、XML ファイルにセーブします?
189 * 引数の isJspInclude によって、jsp:directive.include 処?行うかど?判断します?
190 *
191 * @og.rev 4.2.3.0 (2008/05/26) jsp:directive.include 処??実施可否を引数?します?
192 * @og.rev 5.2.1.0 (2010/10/01) JspIncludeReader#getString の第?引数を?
193 *
194 * @param file 入力XMLファイル
195 * @param isJspInclude jsp:directive.include 処?行うかど??
196 * @see #transform( Reader )
197 */
198 public void transform( final String file, final boolean isJspInclude ) {
199 xmlFile = file;
200
201 if( xmlFile.endsWith( ".jsp" ) && isJspInclude ) {
202 // if( xmlFile.endsWith( ".jsp" ) ) {
203 // transform( new JspIncludeReader().getReader( new File( xmlFile ),encode ) );
204 // jspIncludeData = new JspIncludeReader().getString( new File( xmlFile ),encode,false );
205 jspIncludeData = new JspIncludeReader().getString( new File( xmlFile ),encode ); // 5.2.1.0 (2010/10/01)
206 transform( new StringReader( jspIncludeData ) );
207 }
208 else {
209 transform( FileUtil.getBufferedReader( new File( xmlFile ),encode ) );
210 }
211 }
212
213 /**
214 * XML ファイルをXSLT変換します?
215 * XML 入力リー??に、XSL 入力リー??を適用して?XSLT変換を行います?
216 * 結果は、XML ライターに書き?します?
217 * こ?処??終?に、?力XML リー?? は、close() されます?
218 *
219 * @og.rev 5.6.5.2 (2013/06/21) エラーメ?ージが判りにくいので、追記します?
220 *
221 * @param xmlReader 入力XML リー??
222 * @see #transform( String )
223 */
224 public void transform( final Reader xmlReader ) {
225 HybsEntry[] entry = null;
226
227 try {
228 if( transformer == null ) {
229 init();
230 }
231 else {
232 transformer.reset();
233 }
234
235 // 入力XMLファイルのファイル??を設定します?
236 if( isFileInfo && xmlFile != null ) {
237 entry = getXmlParameter( xmlFile );
238 parameterSet( transformer,entry );
239 }
240 xmlFile = null ;
241
242 // 入力XMLリー??からStreamSourceを作る
243 StreamSource data = new StreamSource( xmlReader );
244
245 transformer.transform( data,result );
246 }
247 catch( TransformerException ex ) {
248 String errMsg = "XML-XSLT 変換に失敗しました? + CR
249 + ex.getMessage() ;
250
251 // 5.6.5.2 (2013/06/21) エラーメ?ージが判りにくいので、追記します?
252 if( errMsg.indexOf( "プロローグにはコン???できません" ) >= 0 ) {
253 errMsg = errMsg + CR + "(UTF-8変換時に、BOMが付くとこ?エラーが?ます?BOMを外してみてください?" ;
254 }
255
256 if( isErrXmlIn ) { setOutData( toXmlRow( entry, ex ) ); }
257
258 if( isErrClose ) { close(); }
259 throw new RuntimeException( errMsg,ex );
260 }
261 finally {
262 Closer.ioClose( xmlReader );
263 }
264 }
265
266 /**
267 * Transformer オブジェクトに対して、Parameter を設定します?
268 *
269 * ?されたパラメーターキーは、xsl ファイルでは、xsl:param で宣??
270 * xsl:value-of で取り出します?
271 * <xsl:param name="ADDRESS" select="" /> と宣?ておき、?な?で
272 * <xsl:value-of select="$ADDRESS" /> とすれば、取得できます?
273 *
274 * @param entry HybsEntry配?
275 */
276 public void setParamEntry( final HybsEntry[] entry ) {
277 if( entry != null && entry.length > 0 ) {
278 paramEntry = new HybsEntry[entry.length];
279 System.arraycopy( entry,0,paramEntry,0,entry.length );
280 }
281 }
282
283 /**
284 * transform 処?にエラーが発生した?合に、?力ファイルを閉じるかど?を指定します?
285 *
286 * 処??中でエラーが発生した?合に、そこで処?中断するか?それとも?
287 * 無視して、さらに処?進めるかを?することが可能です?
288 * 継続して処?進めた??合?、?力ファイルを閉じな?め?false ?
289 * 設定します?ただし?エラー時には、RuntimeException は throw されます?
290 * 初期値は、true(閉じ?です?
291 *
292 * @param flag エラー時クローズ [true:閉じ?false:閉じない]
293 */
294 public void errClose( final boolean flag ) {
295 isErrClose = flag ;
296 }
297
298 /**
299 * transform 処?エラーを?出力ファイルに、XML形式でエラーを追記するかど?を指定します?
300 *
301 * 処??中でエラーが発生した?合に、ログ?ではなく?結果XMLファイルに?
302 * エラー???エラーファイルなどを埋め込?、XMLファイルとしてDB登録??
303 * そ?他集計等に使えます?
304 * 今?、GE70 スキーマ形式?ファイルしか作?できません?
305 * これは?errClose( boolean ) メソ?と共に使用すると効果的です?
306 * つまり?errClose = false; にして、エラー時でも?力ファイルを閉じずに?
307 * 処?続ける事で、エラーメ?ージもXMLファイルとして?できます?
308 * 初期値は、false(使用しな?です?
309 *
310 * @param flag エラー時XML形?[false:使用しな?true:使用する]
311 */
312 public void useErrXmlIn( final boolean flag ) {
313 isErrXmlIn = flag ;
314 }
315
316 /**
317 * jsp:directive.include 発見時に、そのファイル?INCLUDE するかを?するかど?を指定しま?初期値:true:使用する)
318 *
319 * 引数の処?象ファイル(transformの引数ファイル)が??jsp』?場合?
320 * jsp:directive.include 発見時に、そのファイル?INCLUDE するかを?するか
321 * ど?を指定します?
322 * インクルードされたファイルとあわせて、正規?XML にならな?、パーサー
323 * エラーが発生します?
324 * JSPソース解析を行うには、INCLUDE ファイルも?慮しな?正確な結果?
325 * 得られませんが?INCLUDE 先?ファイルまで合わせる?があるため?
326 * 場合によっては、INCLUDEファイルを無視しなければならな?ースがあります?
327 * 初期値は、true(使用する)です?
328 *
329 * @param flag エラー時XML形?[false:使用しな?true:使用する]
330 */
331 public void jspInclude( final boolean flag ) {
332 isInclude = flag ;
333 }
334
335 /**
336 * 入力XSLファイルのストリー?閉じます?
337 *
338 */
339 public void close() {
340 Closer.ioClose( outWriter );
341 }
342
343 /**
344 * XML ファイルをXSLT変換します?
345 * XML 入力ファイルに、XSL 入力ファイルを適用して?XSLT変換を行います?
346 * 結果は、XML ファイルにセーブします?
347 * なお?結果XML ファイル(outFile) に、特別な名称 "System.out" ??を渡すと?
348 * 標準?力に 結果を?力します?
349 */
350 private void init() {
351 try {
352 // xsl属?からStreamSourceを作る
353 StreamSource style = new StreamSource( xslReader );
354
355 // Transformerを作り、XMLを変換する
356 TransformerFactory tFactory = TransformerFactory.newInstance();
357 transformer = tFactory.newTransformer( style );
358
359 parameterSet( transformer,paramEntry );
360 }
361 catch( TransformerConfigurationException ex ) {
362 String errMsg = xslFile + "ファイルの XSLT 解析に失敗しました? + CR
363 + ex.getMessage() ;
364 throw new RuntimeException( errMsg,ex );
365 }
366 finally {
367 Closer.ioClose( xslReader );
368 xslReader = null;
369 }
370 }
371
372 /**
373 * 実行中の入力ファイル名などの属????パラメータとして設定するかど?を指定します?
374 *
375 * こ?パ?サーでは、?部で実行中の入力ファイル???パラメータとして設定できます?
376 * useFileInfo( true ) とセ?すると、以下???目が?部?セ?されます?
377 *
378 * 入力ファイル(inXMLのフルパス) : FILEPATH (? G:\webapps\gf\jsp\DOC10\query.jsp)
379 * 入力親フォル?inXMLの親フォル? : ADDRESS (? DOC10)
380 * 入力ファイル(inXMLのファイル? : FILENAME (? query.jsp)
381 * 入力ファイル(inXMLの更新日? ) : MODIFIED (? yyyyMMddHHmmss形?
382 *
383 * @og.rev 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追??
384 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します?
385 *
386 * xsl ファイルでは、xsl:param で宣?、xsl:value-of で取り出します?
387 * <xsl:param name="ADDRESS" select="" /> と宣?ておき、?な?で
388 * <xsl:value-of select="$ADDRESS" /> とすれば、取得できます?
389 *
390 * 初期値は、false(セ?しな? です?
391 *
392 * @param flag セ?する:true/セ?しな?false
393 */
394 public void useFileInfo( final boolean flag ) {
395 isFileInfo = flag;
396 // if( isFileInfo ) {
397 // formatter = new SimpleDateFormat( "yyyyMMddHHmmss",Locale.JAPAN );
398 // }
399 }
400
401 /**
402 * ファイル名指定で XML,XSL,OUTファイルを指定する?合?エンコードを?します?
403 *
404 * 初期値は、UTF-8 です?
405 *
406 * @param encode エンコー?
407 */
408 public void useEncode( final String encode ) {
409 this.encode = encode;
410 }
411
412 /**
413 * 実行中の入力ファイル名などの属????パラメータとして取得します?
414 *
415 * 入力ファイル(inXMLのフルパス) : FILEPATH (? G:\webapps\gf\jsp\DOC10\query.jsp)
416 * 入力ファイル(inXMLのファイル? : FILENAME (? query.jsp)
417 * 入力親フォル?inXMLの親フォル? : ADDRESS (? DOC10)
418 * 入力ファイル(inXMLの更新日? ) : MODIFIED (? yyyyMMddHHmmss形?
419 *
420 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します?
421 *
422 * @param xmlIn XML入力ファイル
423 *
424 * @return HybsEntry配?
425 */
426 private HybsEntry[] getXmlParameter( final String xmlIn ) {
427 HybsEntry[] entry = new HybsEntry[4] ;
428
429 entry[0] = new HybsEntry( "FILEPATH" , xmlIn) ;
430
431 File xmlFile = new File( xmlIn );
432 entry[1] = new HybsEntry( "FILENAME" , xmlFile.getName()) ;
433
434 File parentFile = xmlFile.getParentFile() ;
435 if( parentFile != null ) {
436 entry[2] = new HybsEntry( "ADDRESS" , parentFile.getName()) ;
437 }
438 else {
439 entry[2] = new HybsEntry( "ADDRESS" , "" ) ;
440 }
441
442 // String lastDate = formatter.format( new Date( xmlFile.lastModified() ) ) ;
443 String lastDate = HybsDateUtil.getDate( xmlFile.lastModified() , "yyyyMMddHHmmss" ) ; // 5.5.7.2 (2012/10/09) HybsDateUtil を利用
444 entry[3] = new HybsEntry( "MODIFIED" , lastDate ) ;
445
446 return entry ;
447 }
448
449 /**
450 * Transformer オブジェク?に、パラメータを設定します?
451 *
452 * ?されたパラメーターキーは、xsl ファイルでは、xsl:param で宣??
453 * xsl:value-of で取り出します?
454 * <xsl:param name="ADDRESS" select="" /> と宣?ておき、?な?で
455 * <xsl:value-of select="$ADDRESS" /> とすれば、取得できます?
456 *
457 * @param former Transformerオブジェク?
458 * @param entry パラメータ配?
459 */
460 private void parameterSet( final Transformer former,final HybsEntry[] entry ) {
461 if( entry != null ) {
462 int size = entry.length;
463 for( int i=0; i<size; i++ ) {
464 String key = entry[i].getKey() ;
465 String val = entry[i].getValue();
466 former.setParameter( key , val );
467 }
468 }
469 }
470
471 /**
472 * こ?オブジェクト????表現を返します?
473 *
474 * 接続URL + "," + 接続ユーザー + " (" + 作?日?+ ")" です?
475 *
476 * @return ???表現
477 */
478 @Override
479 public String toString() {
480 StringBuilder buf = new StringBuilder();
481
482 buf.append( "XSL File:" ).append( xslFile ).append( CR );
483 buf.append( "XML File:" ).append( xmlFile ).append( CR );
484 buf.append( "OUT File:" ).append( outFile ).append( CR );
485
486 return buf.toString() ;
487 }
488
489 /**
490 * エラー??の?XML??表現を返します?
491 *
492 * エラー時???も?XML化して保存する為の簡易???
493 * ここでは、XMLスキーマ?、固定で、GF70 の形式になります?
494 *
495 * @og.rev 4.2.3.0 (2008/05/26) エラー発生時のXMLファイルを追?ます?
496 * @og.rev 5.2.1.0 (2010/10/01) XML形式を変更します?(TEXT⇒TEXT_DATA)
497 *
498 * @param entry HybsEntry配?
499 * @param ex エラー??
500 *
501 * @return XMLの部?字?
502 */
503 private String toXmlRow( final HybsEntry[] entry,final TransformerException ex ) {
504 StringBuilder buf = new StringBuilder();
505
506 buf.append( "<ROW>" ).append( CR );
507 if( paramEntry != null ) {
508 for( int i=0; i<paramEntry.length; i++ ) {
509 String key = paramEntry[i].getKey() ;
510 String val = paramEntry[i].getValue();
511 buf.append( " <" ).append( key ).append( ">" );
512 buf.append( val );
513 buf.append( "</" ).append( key ).append( ">" );
514 buf.append( CR );
515 }
516 }
517
518 if( entry != null ) {
519 for( int i=0; i<entry.length; i++ ) {
520 String key = entry[i].getKey() ;
521 String val = entry[i].getValue();
522 buf.append( " <" ).append( key ).append( ">" );
523 buf.append( val );
524 buf.append( "</" ).append( key ).append( ">" );
525 buf.append( CR );
526 }
527 }
528
529 buf.append( " <TAGNAME />" ).append( CR );
530 buf.append( " <MSGCD>XML_ERROR</MSGCD>" ).append( CR );
531 buf.append( " <MSGTXT>XML-XSLT 変換に失敗しました?/MSGTXT>" ).append( CR );
532
533 String errMsg = StringUtil.htmlFilter( ex.getMessage() );
534 int indx = errMsg.lastIndexOf( "Exception:" );
535 if( indx >= 0 ) {
536 errMsg = errMsg.substring( indx + "Exception:".length() );
537 }
538 buf.append( " <TEXT_DATA>" ).append( errMsg ).append( CR ); // 5.2.1.0 (2010/10/01)
539 buf.append( " Location:" ).append( ex.getLocationAsString() ).append( CR );
540
541 // 4.2.3.0 (2008/05/26)
542 // if( jspIncludeData != null ) {
543 // buf.append( StringUtil.htmlFilter( jspIncludeData ) );
544 // }
545
546 buf.append( "</TEXT_DATA>" ).append( CR ); // 5.2.1.0 (2010/10/01)
547 buf.append( "</ROW>" ).append( CR );
548
549 return buf.toString() ;
550
551 /*
552 <ROW>
553 <SYSTEM_ID> </SYSTEM_ID>
554 <ADDRESS > </ADDRESS>
555 <FILENAME > </FILENAME>
556 <FILEPATH > </FILEPATH>
557 <MODIFIED > </MODIFIED>
558 <TAGNAME > </TAGNAME>
559 <MSGCD > </MSGCD>
560 <MSGTXT > </MSGTXT>
561 <TEXT_DATA> </TEXT_DATA>
562 </ROW>
563 */
564 }
565
566 /**
567 * ?ト用のメインメソ?
568 *
569 * java XSLT in_xml in_xsl out_xml
570 *
571 * @param args コマンド引数配?
572 */
573 public static void main( final String[] args ) throws IOException {
574 if( args.length != 3 ) {
575 LogWriter.log( "Usage: java XSLT in_xml in_xsl out_xml" );
576 LogWriter.log( " XML 入力ファイルに、XSL 入力ファイルを適用して? );
577 LogWriter.log( " XSLT変換を行います?" );
578 LogWriter.log( " 結果は、XML ファイルにセーブします?" );
579 LogWriter.log( " out_xml に System.out を指定すると標準?力に出力します?" );
580 return ;
581 }
582
583 XSLT xslt = new XSLT();
584 xslt.setXslFile( args[1] );
585 xslt.setOutFile( args[2],false );
586 xslt.transform( args[0] );
587 xslt.close();
588 }
589 }