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.util.FileUtil; 021import org.opengion.fukurou.util.Closer ; 022import org.opengion.fukurou.util.LogWriter; 023 024import java.util.Map ; 025import java.util.LinkedHashMap ; 026 027import java.io.File; 028import java.io.PrintWriter; 029 030/** 031 * Process_TableWriter は、上流から受け取ったデータをファイルに書き込む 032 * CainProcess インターフェースの実装クラスです。 033 * 034 * 上流(プロセスチェインのデータは上流から下流へと渡されます。)から 035 * 受け取ったLineModel を元に、DBTableModel 形式ファイルを出力します。 036 * 037 * 引数文字列中にスペースを含む場合は、ダブルコーテーション("") で括って下さい。 038 * 引数文字列の 『=』の前後には、スペースは挟めません。必ず、-key=value の様に 039 * 繋げてください。 040 * 041 * @og.formSample 042 * Process_TableWriter -outfile=OUTFILE -sep=, -encode=UTF-8 -append=true 043 * 044 * -outfile=出力ファイル名 :出力ファイル名 045 * [-sep=セパレータ文字 ] :区切り文字(初期値:タブ) 046 * [-encode=文字エンコード ] :出力ファイルのエンコードタイプ 047 * [-append=[false/true] ] :出力ファイルを、追記する(true)か新規作成する(false)か。 048 * [-useHeader=[true/false] ] :ヘッダー情報(#NAME行)を出力する(true)か出力しない(false)か。 049 * [-useNumber=[true/false] ] :行番号を出力する(true)か出力しない(false)か。 050 * [-useWquot=[false/true] ] :出力データをダブルクオーテーションで括る(true)かそのまま(false)か。 051 * [-omitCTRL=[false/true] ] :コントロール文字を削除する(true)かそのまま(false)か。 052 * [-const_XXXX=固定値 ] :-const_FGJ=1 053 * LineModel のキー(const_ に続く文字列)の値に、固定値を設定します。 054 * キーが異なれば、複数のカラム名を指定できます。 055 * [-display=[false/true] ] :結果を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 056 * [-debug=[false/true] ] :デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 057 * 058 * @version 4.0 059 * @author Kazuhiko Hasegawa 060 * @since JDK5.0, 061 */ 062public class Process_TableWriter extends AbstractProcess implements ChainProcess { 063 private static final String CNST_KEY = "const_" ; 064 065 private String outfile = null; 066 private PrintWriter writer = null; 067 private String separator = TAB; // 項目区切り文字 068 069 private String[] cnstClm = null; // 固定値を設定するカラム名 070 private int[] cnstClmNos = null; // 固定値を設定するのカラム番号 071 private String[] constVal = null; // カラム番号に対応した固定値 072 private File file = null; // 出力ファイル 073 private String encode = System.getProperty("file.encoding"); // 出力ファイルエンコード 074 private boolean append = false; // ファイル追加(true:追加/false:通常) 075 private boolean useHeader = true; // ヘッダー情報(#NAME行)を出力する(true)か出力しない(false)か。 076 private boolean useNumber = true; // 行番号を出力する(true)か出力しない(false)か。 077 private boolean useWquot = false; // 出力データをダブルクオーテーションで括る(true)かそのまま(false)か。 078 private boolean omitCTRL = false; // コントロール文字を削除する(true)かそのまま(false)か。 079 private boolean display = false; // 表示しない 080 private boolean debug = false; // 5.7.3.0 (2014/02/07) デバッグ情報 081 082 private boolean firstRow = true; // 最初の一行目 083 private int count = 0; 084 085 private static final Map<String,String> mustProparty ; // [プロパティ]必須チェック用 Map 086 private static final Map<String,String> usableProparty ; // [プロパティ]整合性チェック Map 087 088 static { 089 mustProparty = new LinkedHashMap<String,String>(); 090 mustProparty.put( "outfile", "出力ファイル名 (必須)" ); 091 092 usableProparty = new LinkedHashMap<String,String>(); 093 usableProparty.put( "sep", "区切り文字(初期値:タブ)" ); 094 usableProparty.put( "encode", "出力ファイルのエンコードタイプ" ); 095 usableProparty.put( "append", "出力ファイルを、追記する(true)か新規作成する(false)か。" ); 096 usableProparty.put( "useHeader", "ヘッダー情報(#NAME行)を出力する(true)か出力しない(false)か。" ); 097 usableProparty.put( "useNumber", "行番号を出力する(true)か出力しない(false)か。" ); 098 usableProparty.put( "useWquot", "出力データをダブルクオーテーションで括る(true)かそのまま(false)か。" ); 099 usableProparty.put( "omitCTRL", "コントロール文字を削除する(true)かそのまま(false)か。" ); 100 usableProparty.put( "const_", "LineModel のキー(const_ に続く文字列)の値に、固定値を" + 101 CR + "設定します。キーが異なれば、複数のカラム名を指定できます。" + 102 CR + "例: -const_FGJ=1" ); 103 usableProparty.put( "display", "結果を標準出力に表示する(true)かしない(false)か" + 104 CR + " (初期値:false:表示しない)" ); 105 usableProparty.put( "debug", "デバッグ情報を標準出力に表示する(true)かしない(false)か" + 106 CR + "(初期値:false:表示しない)" ); // 5.7.3.0 (2014/02/07) デバッグ情報 107 } 108 109 /** 110 * デフォルトコンストラクター。 111 * このクラスは、動的作成されます。デフォルトコンストラクターで、 112 * super クラスに対して、必要な初期化を行っておきます。 113 * 114 */ 115 public Process_TableWriter() { 116 super( "org.opengion.fukurou.process.Process_TableWriter",mustProparty,usableProparty ); 117 } 118 119 /** 120 * プロセスの初期化を行います。初めに一度だけ、呼び出されます。 121 * 初期処理(ファイルオープン、DBオープン等)に使用します。 122 * 123 * @param paramProcess データベースの接続先情報などを持っているオブジェクト 124 */ 125 public void init( final ParamProcess paramProcess ) { 126 Argument arg = getArgument(); 127 128 outfile = arg.getProparty("outfile"); 129 encode = arg.getProparty("encode",encode); 130 separator = arg.getProparty("sep",separator ); 131 append = arg.getProparty("append",append); 132 useHeader = arg.getProparty("useHeader",useHeader); 133 useNumber = arg.getProparty("useNumber",useNumber); 134 useWquot = arg.getProparty("useWquot",useWquot); 135 omitCTRL = arg.getProparty("omitCTRL",omitCTRL); 136 HybsEntry[] cnstKey = arg.getEntrys( CNST_KEY ); // 配列 137 display = arg.getProparty("display",display); 138 debug = arg.getProparty("debug",debug); // 5.7.3.0 (2014/02/07) デバッグ情報 139 140 int size = cnstKey.length; 141 cnstClm = new String[size]; 142 constVal = new String[size]; 143 for( int i=0; i<size; i++ ) { 144 cnstClm[i] = cnstKey[i].getKey(); 145 constVal[i] = cnstKey[i].getValue(); 146 } 147 148 if( outfile == null ) { 149 String errMsg = "ファイル名が指定されていません。" ; 150 throw new RuntimeException( errMsg ); 151 } 152 153 file = new File( outfile ); 154 File dir = file.getParentFile() ; 155 156 // ディレクトリが存在しない場合の処理 157 if( ! dir.exists() && ! dir.mkdirs() ) { 158 String errMsg = "ディレクトリが作成できませんでした。[" + dir + "]" ; 159 throw new RuntimeException( errMsg ); 160 } 161 } 162 163 /** 164 * プロセスの終了を行います。最後に一度だけ、呼び出されます。 165 * 終了処理(ファイルクローズ、DBクローズ等)に使用します。 166 * 167 * @param isOK トータルで、OKだったかどうか[true:成功/false:失敗] 168 */ 169 public void end( final boolean isOK ) { 170 if( writer != null ) { 171 writer.flush(); 172 Closer.ioClose( writer ); 173 writer = null; 174 } 175 } 176 177 /** 178 * 引数の LineModel を処理するメソッドです。 179 * 変換処理後の LineModel を返します。 180 * 後続処理を行わない場合(データのフィルタリングを行う場合)は、 181 * null データを返します。つまり、null データは、後続処理を行わない 182 * フラグの代わりにも使用しています。 183 * なお、変換処理後の LineModel と、オリジナルの LineModel が、 184 * 同一か、コピー(クローン)かは、各処理メソッド内で決めています。 185 * ドキュメントに明記されていない場合は、副作用が問題になる場合は、 186 * 各処理ごとに自分でコピー(クローン)して下さい。 187 * 188 * @param data オリジナルのLineModel 189 * 190 * @return 処理変換後のLineModel 191 */ 192 public LineModel action( final LineModel data ) { 193 count++ ; 194 // if( display ) { println( data.dataLine() ); } 195 if( firstRow ) { 196 writer = FileUtil.getPrintWriter( file,encode,append ); 197 if( useHeader && useNumber ) { writeName( data ); } 198 199 int size = cnstClm.length; 200 cnstClmNos = new int[size]; 201 for( int i=0; i<size; i++ ) { 202 cnstClmNos[i] = data.getColumnNo( cnstClm[i] ); 203 } 204 205 firstRow = false; 206 if( display ) { println( data.nameLine() ); } // 5.7.3.0 (2014/02/07) デバッグ情報 207 } 208 209 // 固定値置き換え処理 210 for( int j=0; j<cnstClmNos.length; j++ ) { 211 data.setValue( cnstClmNos[j],constVal[j] ); 212 } 213 214 writeData( data ); 215 216 if( display ) { println( data.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 217 return data; 218 } 219 220 /** 221 * PrintWriter に LineModelの項目名情報を書き込みます。 222 * 第一カラム目は、項目名情報を示す "#Name" を書き込みます。 223 * この行は、出力形式に無関係に、TAB で区切られます。 224 * 225 * @param data ラインモデル 226 */ 227 private void writeName( final LineModel data ) { 228 int size = data.size(); 229 writer.print( "#Name" ); 230 for( int clm=0; clm<size; clm++ ) { 231 writer.print( TAB ); 232 writer.print( data.getName(clm) ); 233 } 234 writer.println(); 235 } 236 237 /** 238 * PrintWriter に LineModelのテーブル情報を書き込みます。 239 * 240 * @og.rev 5.2.2.0 (2010/11/01) 改行を含む場合は、ダブルクオートを強制的に前後に追加する。 241 * @og.rev 5.2.2.0 (2010/11/01) ダブルクオートを含む場合は、その直前にダブルクオートを強制的に追加する。 242 * 243 * @param data ラインモデル 244 */ 245 private void writeData( final LineModel data ) { 246 int size = data.size(); 247 248 if( useNumber ) { writer.print( data.getRowNo() ); } // 行番号 249 for( int clm=0; clm<size; clm++ ) { 250 if( useNumber || clm!=0 ) { writer.print( separator ); } 251 Object val = data.getValue(clm); 252 if( val == null ) { val = ""; } 253 254 String sval = String.valueOf( val ); 255 // 5.2.2.0 (2010/11/01) ダブルクオートを含む場合は、その直前にダブルクオートを強制的に追加する。 256 if( sval.indexOf( '"' ) >= 0 ) { sval = sval.replaceAll( "\"" ,"\"\"" ) ; } 257 if( omitCTRL ) { sval = sval.replaceAll( "\\s" ," " ) ; } 258 // 5.2.2.0 (2010/11/01) 改行を含む場合は、ダブルクオートを強制的に前後に追加する。 259 if( useWquot || ( !omitCTRL && sval.indexOf( CR ) >= 0 ) ) { 260 sval = "\"" + sval + "\"" ; 261 } 262 writer.print( sval ); 263 } 264 writer.println(); 265 } 266 267 /** 268 * プロセスの処理結果のレポート表現を返します。 269 * 処理プログラム名、入力件数、出力件数などの情報です。 270 * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような 271 * 形式で出してください。 272 * 273 * @return 処理結果のレポート 274 */ 275 public String report() { 276 String report = "[" + getClass().getName() + "]" + CR 277 + TAB + "Output File : " + outfile + CR 278 + TAB + "Output Count : " + count ; 279 280 return report ; 281 } 282 283 /** 284 * このクラスの使用方法を返します。 285 * 286 * @return このクラスの使用方法 287 */ 288 public String usage() { 289 StringBuilder buf = new StringBuilder(); 290 291 buf.append( "Process_TableWriter は、上流から受け取ったデータをファイルに書き込む" ).append( CR ); 292 buf.append( "CainProcess インターフェースの実装クラスです。" ).append( CR ); 293 buf.append( CR ); 294 buf.append( "上流(プロセスチェインのデータは上流から下流へと渡されます。)から" ).append( CR ); 295 buf.append( "受け取ったLineModel を元に、DBTableModel 形式ファイルを出力します。" ).append( CR ); 296 buf.append( CR ); 297 buf.append( "引数文字列中に空白を含む場合は、ダブルコーテーション(\"\") で括って下さい。" ).append( CR ); 298 buf.append( "引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に" ).append( CR ); 299 buf.append( "繋げてください。" ).append( CR ); 300 buf.append( CR ).append( CR ); 301 302 buf.append( getArgument().usage() ).append( CR ); 303 304 return buf.toString(); 305 } 306 307 /** 308 * このクラスは、main メソッドから実行できません。 309 * 310 * @param args コマンド引数配列 311 */ 312 public static void main( final String[] args ) { 313 LogWriter.log( new Process_TableWriter().usage() ); 314 } 315}