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