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.plugin.table; 017 018import java.io.File; 019import java.io.PrintWriter; 020import java.util.Locale; 021 022import org.opengion.fukurou.system.OgBuilder ; // 6.4.4.1 (2016/03/18) 023import org.opengion.fukurou.db.DBUtil; 024import org.opengion.fukurou.db.Transaction; // 5.5.2.6 (2012/05/25) 025import org.opengion.fukurou.util.ErrorMessage; 026import org.opengion.fukurou.util.FileUtil; 027import org.opengion.fukurou.util.FixLengthData; 028import org.opengion.fukurou.util.StringUtil; 029import org.opengion.hayabusa.common.HybsSystem; 030import org.opengion.hayabusa.common.HybsSystemException; 031import org.opengion.hayabusa.db.AbstractTableFilter; 032import org.opengion.hayabusa.db.DBTableModel; 033 034/** 035 * TableFilter_TABLE は、TableFilter インターフェースを継承した、DBTableModel 処理用の 036 * 実装クラスです。 037 * 038 * ここでは、テーブル一覧の検索結果より、GF05 のテーブルカラム定義テーブルから 039 * 必要な情報を取得し、テーブル作成スクリプトを作成します。 040 * 出力ファイルは、テーブル名+"S.sql" という命名規則で作成します。 041 * 検索では、(SYSTEM_ID,TBLSYU,TABLE_NAME,NAME_JA,TABLESPACE_NAME,INITIAL_EXTENT,COMMENTS) 042 * の項目を取得する必要があります。 043 * 044 * 6.1.0.0 (2014/12/26) より、NEXT_EXTENT は使用しなくなりました。 045 * 046 * パラメータは、tableFilterタグの keys, vals にそれぞれ記述するか、BODY 部にCSS形式で記述します。 047 * 【パラメータ】 048 * { 049 * DIR : {@BASE_DIR}/sql/install/01_TABLE ; 出力ファイルの基準フォルダ(必須) 050 * XML : false ; XML出力を行うかどうか[true/false]を指定します(初期値:false)。 051 * } 052 * 053 * @og.formSample 054 * ●形式: 055 * select SYSTEM_ID,TBLSYU,TABLE_NAME,NAME_JA,TABLESPACE_NAME,INITIAL_EXTENT,COMMENTS from GF02 056 * 057 * ① <og:tableFilter classId="TABLE" keys="DIR,XML" vals='"{@BASE_DIR}/sql/install/01_TABLE,"' /> 058 * 059 * ② <og:tableFilter classId="TABLE" > 060 * { 061 * DIR : {@BASE_DIR}/sql/install/01_TABLE ; 062 * XML : false ; 063 * } 064 * </og:tableFilter> 065 * 066 * @og.rev 4.0.0.0 (2005/08/31) 新規作成 067 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを追加 068 * 069 * @version 0.9.0 2000/10/17 070 * @author Kazuhiko Hasegawa 071 * @since JDK1.1, 072 */ 073public class TableFilter_TABLE extends AbstractTableFilter { 074 /** このプログラムのVERSION文字列を設定します。 {@value} */ 075 private static final String VERSION = "6.5.0.1 (2016/10/21)" ; 076 077 // 6.1.0.0 (2014/12/26) NEXT_EXTENT は、使いません。 078 private static final String[] DBKEY = {"SYSTEM_ID","TBLSYU","TABLE_NAME","NAME_JA", 079 "TABLESPACE_NAME","INITIAL_EXTENT","COMMENTS" }; 080 081 // 5.1.1.0 (2009/12/01) データのアクセス用の配列番号のIDを private ⇒ protected にします。 082 /** データのアクセス用の配列番号 {@value} */ 083 protected static final int SYSTEM_ID = 0; 084 /** データのアクセス用の配列番号 {@value} */ 085 protected static final int TBLSYU = 1; 086 /** データのアクセス用の配列番号 {@value} */ 087 protected static final int TABLE_NAME = 2; 088 /** データのアクセス用の配列番号 {@value} */ 089 protected static final int NAME_JA = 3; 090 /** データのアクセス用の配列番号 {@value} */ 091 protected static final int TABLESPACE_NAME = 4; 092 /** データのアクセス用の配列番号 {@value} */ 093 protected static final int INITIAL_EXTENT = 5; 094 /** データのアクセス用の配列番号 {@value} */ 095 protected static final int COMMENTS = 6; // 6.1.0.0 (2014/12/26) NEXT_EXTENT は、使いません。 096 097 private static final String GF05_SEL = "SELECT CLM,SEQNO,NAME_JA,CLS_NAME,USE_LENGTH,DATA_DEFAULT,NOT_NULL,'' AS OPTS" 098 + " FROM GF05" 099 + " WHERE SYSTEM_ID=? AND TBLSYU=? AND TABLE_NAME=?" 100 + " AND FGJ='1'" 101 + " ORDER BY SEQNO" ; 102 103 /** データのアクセス用の配列番号 {@value} */ 104 protected static final int GF05_CLM = 0; 105 /** データのアクセス用の配列番号 {@value} */ 106 protected static final int GF05_SEQNO = 1; 107 /** データのアクセス用の配列番号 {@value} */ 108 protected static final int GF05_NAME_JA = 2; 109 /** データのアクセス用の配列番号 {@value} */ 110 protected static final int GF05_CLS_NAME = 3; 111 /** データのアクセス用の配列番号 {@value} */ 112 protected static final int GF05_USE_LENGTH = 4; 113 /** データのアクセス用の配列番号 {@value} */ 114 protected static final int GF05_DATA_DEFAULT= 5; 115 /** データのアクセス用の配列番号 {@value} */ 116 protected static final int GF05_NOT_NULL = 6; 117 /** データのアクセス用の配列番号 {@value} */ 118 protected static final int GF05_OPTIONS = 7; 119 120 // private static final String ENCODE = "Windows-31J" ; 121 private static final String ENCODE = "UTF-8" ; // 4.3.6.6 (2009/05/15) 122 123 private static final String CMNT = "************************************************************************" ; 124 125 private static final int X = FixLengthData.X ; // type 定数 126 private static final int S = FixLengthData.S ; // type 定数 127 private static final int K = FixLengthData.K ; // type 定数 128 private static final int T = FixLengthData.T ; // addLen 定数 129 private static final int T2= FixLengthData.T2 ; // addLen 定数 130 131 /** 各種定数 */ 132 // 6.0.2.3 (2014/10/10) AbstractTableFilter へ移動 133 134 /** XML形式かどうか */ 135 136 /** 137 * デフォルトコンストラクター 138 * 139 * @og.rev 6.4.1.1 (2016/01/16) keysMap を、サブクラスから設定させるように変更。 140 */ 141 public TableFilter_TABLE() { 142 super(); 143 initSet( "DIR" , "出力ファイルの基準フォルダ(必須)" ); 144 initSet( "XML" , "XML出力を行うかどうか[true/false]を指定(初期値:false)" ); 145 } 146 147 /** 148 * DBTableModel処理を実行します。 149 * 150 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定 151 * @og.rev 4.0.0.0 (2007/11/28) メソッドの戻り値をチェックします。 152 * @og.rev 4.3.7.0 (2009/06/01) トリガー、SEQUENCE作成機能、XML出力機能追加 153 * @og.rev 5.1.1.0 (2009/12/01) XML_START_TAG に、tableName をセットします。 154 * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応 155 * @og.rev 5.5.2.6 (2012/05/25) protected変数を、private化したため、getterメソッドで取得するように変更 156 * @og.rev 5.6.6.0 (2013/07/05) FixLengthData の簡易コンストラクタを使用 157 * @og.rev 6.0.2.3 (2014/10/10) isXml で、CR + EXEC_END_TAG のキャッシュを作成します。 158 * @og.rev 6.3.7.0 (2015/09/04) AutoCloseableを使用したtry-with-resources構築に対応。 159 * @og.rev 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。 160 * 161 * @return 実行結果のテーブルモデル 162 */ 163 public DBTableModel execute() { 164 isXml = StringUtil.nval( getValue( "XML" ), false ); 165 166 execEndTag = isXml ? CR + EXEC_END_TAG : ";" ; // 6.0.2.3 (2014/10/10) 167 168 final File dir = new File( getValue( "DIR" ) ); 169 if( ! dir.exists() && ! dir.mkdirs() ) { 170 final String errMsg = "所定のフォルダが作成できませんでした。[" + dir + "]" ; 171 // 4.3.4.4 (2009/01/01) 172 throw new HybsSystemException( errMsg ); 173 } 174 175 // カンマ,カラム,クラス,(,桁数,),初期値,NOT_NULL,拡張機能,コメント開始,行番号,名称,コメント終了 176 final int[] addLen = new int[] { 0,T,0,0,0,T2,T,T,T2,0,1,T,0 }; // 各データ間のスペース 177 final int[] type = new int[] { X,X,X,X,S,X, X,X,X, X,S,K,X }; // 各データの種別 X:半角 S:空白前埋め K:全角混在 178 final FixLengthData fixData = new FixLengthData( addLen,type ); 179 180 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point. 181 final DBTableModel table = getDBTableModel(); // 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加 182 final Transaction tran = getTransaction(); // 5.5.2.6 (2012/05/25) 183 final int[] clmNo = getTableColumnNo( DBKEY ); 184 final int rowCnt = table.getRowCount(); 185 for( int row=0; row<rowCnt; row++ ) { 186 // 6.3.7.0 (2015/09/04) AutoCloseableを使用したtry-with-resources構築に対応。 187 final String[] data = table.getValues( row ); // 6.3.9.1 (2015/11/27) 188 final String systemId = data[clmNo[SYSTEM_ID]]; 189 final String tblsyu = data[clmNo[TBLSYU]]; 190 final String tableName = data[clmNo[TABLE_NAME]]; 191 192 try( final PrintWriter writer = FileUtil.getPrintWriter( new File( dir,tableName + ( isXml ? "S.xml" : "S.sql" ) ),ENCODE ) ) { 193 194 if( isXml ) { writer.println( XML_START_TAG.replace( "xxx",tableName ) ); } // 5.1.1.0 (2009/12/01) tableName をセット 195 writer.print( makeHeadLine( clmNo,data ) ); 196 197 final String[] vals = new String[] { systemId,tblsyu,tableName }; 198 final String[][] gf05 = DBUtil.dbExecute( GF05_SEL,vals,tran ); // 5.1.9.0 (2010/08/01) Transaction 対応 199 200 String uniqName = null; 201 fixData.clear(); 202 // 値セット:まずは、最大長を求める必要がある。 203 for( int i=0; i<gf05.length; i++ ) { 204 final String[] outData = makeLineList( gf05[i],i==0 ); 205 fixData.addListData( outData ); 206 207 // 4.3.7.0 (2009/06/01) 208 if( "UNIQ".equalsIgnoreCase( gf05[i][GF05_CLM] ) || "UNIQSEQ".equalsIgnoreCase( gf05[i][GF05_CLM] ) ) { 209 uniqName = gf05[i][GF05_CLM].toUpperCase( Locale.JAPAN ); 210 } 211 } 212 // 固定長化:最大長であわせた文字列を出力します。 213 for( int i=0; i<gf05.length; i++ ) { 214 writer.println( fixData.getFixData( i ) ); 215 } 216 writer.println( makeEndLine( clmNo,data ) ); 217 218 // 4.3.7.0 (2009/06/01) UNIQ項目のSEQとトリガーを作成 219 if( uniqName != null ) { 220 writer.println( makeUniqSeq( clmNo,data ) ); 221 writer.println( makeUniqTrig( clmNo,data, uniqName ) ); 222 } 223 224 if( isXml ) { writer.println( XML_END_TAG ); } 225 } 226 catch( final RuntimeException ex ) { // catch は、close() されてから呼ばれます。 227 // 6.5.0.1 (2016/10/21) ErrorMessage をまとめるのと、直接 Throwable を渡します。 228 final ErrorMessage errMessage = makeErrorMessage( "TableFilter_TABLE Error",ErrorMessage.NG ) 229 .addMessage( row+1,ErrorMessage.NG,"TABLE" 230 , "TABLE=[" + tableName + "]" 231 , StringUtil.array2csv( data ) 232 ) 233 .addMessage( ex ); 234 235 // BAT から呼び出す場合があるため、標準エラー出力にも情報を出しておきます。 236 System.out.println( errMessage ); 237 } 238 } 239 240 return table; 241 } 242 243 /** 244 * ヘッダー部分の処理を実行します。 245 * 246 * @og.rev 5.6.6.0 (2013/07/05) FixLengthData の簡易コンストラクタを使用 247 * @og.rev 5.6.6.2 (2013/07/19) EXEC_START_TAG の付け忘れ 248 * @og.rev 6.4.4.2 (2016/04/01) StringBuilderの代わりに、OgBuilderを使用する。 249 * 250 * @param clmNo カラム番号配列 251 * @param data 1行分のデータ配列 252 * 253 * @return ヘッダー部分の文字列 254 * @og.rtnNotNull 255 */ 256 protected String makeHeadLine( final int[] clmNo,final String[] data ) { 257 final String TBL_NAME = data[clmNo[TABLE_NAME]]; 258 259 final String LINE1 = TBL_NAME + " ( " + data[clmNo[NAME_JA]] + " )" ; 260 final String LINE2 = data[clmNo[COMMENTS]] ; 261 final String LINE3 = "Created : " + HybsSystem.getDate() ; 262 263 // 5.6.6.0 (2013/07/05) FixLengthData の簡易コンストラクタを使用 264 final int[] addLen = new int[] { 0,0,0 }; // 各データ間のスペース 265 final int[] type = new int[] { X,K,X }; // 各データの種別 X:半角 S:空白前埋め K:全角混在 266 final FixLengthData fixData = new FixLengthData( addLen,type ); 267 268 final String[][] outData = new String[][] { 269 { "/**", CMNT , "**/" }, 270 { "/* ", LINE1, " */" }, 271 { "/* ", LINE2, " */" }, 272 { "/* ", LINE3, " */" }, 273 { "/**", CMNT , "**/" }, 274 }; 275 276 fixData.addAllListData( outData ); 277 278 // 6.4.4.2 (2016/04/01) 279 final OgBuilder buf = new OgBuilder(); 280 fixData.getAllFixData( buf.getBuilder() ); // OgBuilder の内部 Builder に、fixData のデータを書き込む。 281 return buf.appendIfCR( isXml , EXEC_START_TAG ) 282 .appendCR( "CREATE TABLE " , TBL_NAME , " (" ) 283 .toString(); 284 } 285 286 /** 287 * 各行部分(カラム定義)の処理を実行します。 288 * カンマ,カラム,クラス,(,桁数,),初期値,NOT_NULL,拡張機能,コメント開始,行番号,名称,コメント終了 289 * の順に配列にセットします。 290 * 291 * @og.rev 5.5.1.9 (2012/04/18) useLen.length=0対応 292 * @og.rev 5.9.13.2 (2016/10/28) シングルクォート対応 293 * 294 * @param data 1行分のデータ配列 295 * @param first 最初の行かどうか[true:最初/false:それ以降] 296 * 297 * @return 各行部分(カラム定義)配列 298 */ 299 protected String[] makeLineList( final String[] data,final boolean first ) { 300 // カンマ,カラム,クラス(桁数),初期値,NOT_NULL,独自拡張,行番号,名称,終了 301 final String[] outData = new String[13]; // 6.3.9.1 (2015/11/27) 302 final String clsName = data[GF05_CLS_NAME]; 303 304 outData[0] = first ? " " : " , " ; // 0:カンマ 305 outData[1] = data[GF05_CLM] ; // 1:カラム 306 307 if( clsName.startsWith( "CLOB" ) || clsName.startsWith( "DATE" ) ) { 308 data[GF05_USE_LENGTH] = null; 309 } 310 final String useLen = data[GF05_USE_LENGTH]; 311 if( useLen != null && ! useLen.equals( "0" ) && useLen.length() > 0 ) { // 5.5.1.9 (2012/04/18) 312 outData[2] = clsName ; // 2:クラス 313 outData[3] = " ( " ; // 3:( 314 outData[4] = useLen ; // 4:桁数 315 outData[5] = " )" ; // 5:) 316 } 317 else { 318 outData[2] = clsName ; // NUMBER型の桁数指定なしのケース 319 } 320 321 final String def = data[GF05_DATA_DEFAULT]; 322 if( def != null && def.length() > 0 ) { 323 final String comma = clsName.indexOf( "CHAR" ) >= 0 && def.indexOf( '\'' ) < 0 ? "'" : "" ; // 5.9.13.2 (2016/10/28) 324 outData[6] = "DEFAULT " + comma + def + comma ; // 6:初期値 325 } 326 327 final String notNull = data[GF05_NOT_NULL]; 328 if( notNull != null && notNull.equals( "1" ) ) { 329 outData[7] = "NOT NULL" ; // 7:NOT_NULL 330 } 331 332 final String options = data[GF05_OPTIONS]; 333 if( options != null ) { 334 outData[8] = options ; // 8:拡張機能 335 } 336 337 final String nameJA = data[GF05_NAME_JA] ; // 名称 338 if( nameJA != null ) { 339 outData[9] = "/* " ; // 9:コメント開始 340 outData[10] = data[GF05_SEQNO] ; // 10:行番号 341 outData[11] = nameJA ; // 11:名称 342 outData[12] = "*/" ; // 12:コメント終了 343 } 344 345 return outData ; 346 } 347 348 /** 349 * 定義の最後の部分の処理を実行します。 350 * 351 * 6.1.0.0 (2014/12/26) より、 352 * 1.TABLESPACE_NAME を指定しない場合は、TABLESPACE 句を出力しません。 353 * 2.INITIAL_EXTENT を 0 で指定した場合は、STORAGE 句を出力しません。 354 * 3.NEXT と PCTINCREASE は、出力しません。 355 * 356 * @og.rev 6.0.2.3 (2014/10/10) isXml で、CR + EXEC_END_TAG のキャッシュを作成します。 357 * @og.rev 6.1.0.0 (2014/12/26) TABLESPACE_NAME,INITIAL_EXTENT が未設定の場合、設定しません。 358 * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。 359 * 360 * @param clmNo カラム番号配列 361 * @param data 1行分のデータ配列 362 * 363 * @return 定義の最後の部分 364 * @og.rtnNotNull 365 */ 366 protected String makeEndLine( final int[] clmNo,final String[] data ) { 367 368 // 6.1.0.0 (2014/12/26) TABLESPACE_NAME,INITIAL_EXTENT が未設定の場合、設定しません。 369 final String tblSpcse = data[clmNo[TABLESPACE_NAME]] ; 370 final String initExt = data[clmNo[INITIAL_EXTENT]] ; 371 372 return new OgBuilder() 373 .append( ")" ) 374 .appendIf( !StringUtil.isNull( tblSpcse ) 375 , CR , "TABLESPACE " , tblSpcse ) 376 .appendIf( !StringUtil.isNull( initExt ) && initExt.charAt(0) != '0' 377 , CR , "STORAGE( INITIAL " , initExt , "K )" ) 378 .append( execEndTag ) 379 .toString(); 380 } 381 382 /** 383 * ユニークシーケンスの作成処理を実行します。 384 * 385 * @og.rev 5.1.9.0 (2010/08/01) シーケンス名を[TABLE_NAME]S00に変更 386 * @og.rev 6.0.2.3 (2014/10/10) isXml で、CR + EXEC_END_TAG のキャッシュを作成します。 387 * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。 388 * 389 * @param clmNo カラム番号配列 390 * @param data 1行分のデータ配列 391 * 392 * @return ユニークシーケンス 393 * @og.rtnNotNull 394 */ 395 protected String makeUniqSeq( final int[] clmNo,final String[] data ) { 396 return new OgBuilder() 397 .appendCR() 398 .appendIfCR( isXml , EXEC_START_TAG ) 399 .appendCR( "CREATE SEQUENCE " , data[clmNo[TABLE_NAME]] , "S00 " ) 400 .append( " INCREMENT BY 1 START WITH 1 MAXVALUE 999999999 CYCLE NOCACHE" ) 401 .append( execEndTag ) 402 .toString(); 403 } 404 405 /** 406 * ユニークシーケンスと関連付けるトリガの作成処理を実行します。 407 * 408 * @og.rev 5.1.9.0 (2010/08/01) トリガー名を[TABLE_NAME]T00に変更 409 * @og.rev 6.4.4.1 (2016/03/18) StringBuilderの代わりに、OgBuilderを使用する。 410 * 411 * @param clmNo カラム番号配列 412 * @param data 1行分のデータ配列 413 * @param uniqName ユニークトリガ名 414 * 415 * @return ユニークシーケンスと関連付けるトリガ 416 * @og.rtnNotNull 417 */ 418 protected String makeUniqTrig( final int[] clmNo,final String[] data, final String uniqName ) { 419 final String TBL_NAME = data[clmNo[TABLE_NAME]] ; 420 421 return new OgBuilder() 422 .appendCR() 423 .appendIfCR( isXml , EXEC_START_TAG ) 424 .appendCR( "CREATE OR REPLACE TRIGGER " , TBL_NAME , "T00 " ) 425 .appendCR( " BEFORE INSERT ON " , TBL_NAME ) 426 .appendCR( " FOR EACH ROW " ) 427 .appendCR( " BEGIN " ) 428 .append( " SELECT " , TBL_NAME , "S00.NEXTVAL INTO :NEW." ) 429 .appendCR( uniqName , " FROM DUAL; " ) 430 .appendCR( " END; " ) 431 .appendCase( isXml , EXEC_END_TAG , "/" ) // isXmlで選択されます。true/false 432 .toString(); 433 } 434}