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.hayabusa.taglib; 017 018import java.io.IOException; 019import java.nio.file.Files; 020import java.nio.file.Paths; 021import java.nio.charset.StandardCharsets; 022import java.util.Map; 023import java.util.stream.Stream; 024import java.util.stream.Collectors; 025 026import org.opengion.hayabusa.common.HybsSystem; 027import org.opengion.hayabusa.common.HybsSystemException; 028import org.opengion.fukurou.util.JSONScan; 029import org.opengion.fukurou.util.StringUtil; 030import org.opengion.fukurou.util.ToString; 031 032/** 033 * Ior のリクエストJSON に対して、Select文を生成するクラスです。 034 * 035 * 通常は、POSTデータの JSONか、loadFileで指定したファイル形式の JSONを読み込んで、 036 * SQL文を作成するため、この結果を、queryタグ等で実行して、DBTableModel を生成します。 037 * 038 * @og.formSample 039 * ●形式:<og:iorSQL /> 040 * ●body:なし 041 * 042 * POSTデータ、または、loadFile に記載する JSON は、下記の形式です。 043 * { 044 * "report_name": "≪テーブル名≫", 045 * "select_cols": "PN,sum(SUNYSY) AS AAA,max(sys_index),COUNT(*) as cnt,TANI as TANIIII", 046 * "where_lk": "{'TANI':'%%g'}", 047 * "where_gt": "{'DYNYSY':'20210101','SUNYSY':'1'}", 048 * "where_lt": "{'SUNYSY':2000}", 049 * "group_by":"PN,TANI", 050 * "order_by":"TANIIII" 051 * } 052 * 053 * ●Tag定義: 054 * <og:iorSQL 055 * reqKey 【TAG】JSONデータを取り出すPOSTリクエストのキー 056 * loadFile 【TAG】ファイルからJSONデータを読み取ります(指定された場合、ファイル優先) 057 * caseKey 【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 058 * caseVal 【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 059 * caseNN 【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 060 * caseNull 【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 061 * caseIf 【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない) 062 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 063 * /> 064 * 065 * @og.group 画面部品 066 * @og.rev 8.1.1.0 (2022/02/04) 新規追加 067 * 068 * @version 8.1 069 * @author Kazuhiko Hasegawa 070 * @since JDK17, 071 */ 072public class IorSQLTag extends CommonTagSupport { 073 /** このプログラムのVERSION文字列を設定します。 {@value} */ 074 private static final String VERSION = "8.1.1.0 (2022/02/04)" ; 075 private static final long serialVersionUID = 811020220204L ; 076 077 private String fileURL = HybsSystem.sys( "FILE_URL" ); // ファイルURL 078 079 private String jsonVal ; 080 private String reqKey; // POSTリクエストから読み込む場合のキー 081 private String loadFile; // ファイルから読み込む場合のファイル名 082 083 /** 084 * デフォルトコンストラクター 085 * 086 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 087 */ 088 public IorSQLTag() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 089 090 /** 091 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。 092 * 093 * @return 後続処理の指示( EVAL_BODY_BUFFERED ) 094 */ 095 @Override 096 public int doStartTag() { 097 if( useTag() ) { 098 useXssCheck( false ); 099 useQuotCheck( false ); 100 } 101 102 return SKIP_BODY ; // Body を評価しない 103 } 104 105 /** 106 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 107 * 108 * @return 後続処理の指示 109 */ 110 @Override 111 public int doEndTag() { 112 debugPrint(); // 4.0.0 (2005/02/28) 113 114 // 優先順位は、loadFile があればloadFileから。 115 if( loadFile != null ) { 116 // Files.lines には、try-with-resources文 が必要って…こんな感じ? 117 try( Stream<String> srm = Files.lines( Paths.get(loadFile) , StandardCharsets.UTF_8 ) ) { 118 jsonVal = srm.collect( Collectors.joining() ); 119 } 120 catch( final IOException ex ) { 121 final String errMsg = "loadFile 処理中でエラーが発生しました。" + CR 122 + "\t " + ex.getMessage() + CR ; 123 throw new HybsSystemException( errMsg, ex ); 124 } 125 } 126 else if( reqKey != null ) { 127 jsonVal = getRequest().getParameter( reqKey ); 128 } 129 else { 130 final String errMsg = "reqKey か、loadFile は指定してください。" ; 131 throw new HybsSystemException( errMsg ); 132 } 133 134 printSQL() ; // 画面上に生成したSQL文を出力します。 135 136 return EVAL_PAGE ; 137 } 138 139 /** 140 * タグリブオブジェクトをリリースします。 141 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 142 * 143 */ 144 @Override 145 protected void release2() { 146 super.release2(); 147 jsonVal = null; 148 reqKey = null; // POSTリクエストから読み込む場合のキー 149 loadFile = null; // ファイルから読み込む場合のファイル名 150 } 151 152 /** 153 * タグリブオブジェクトをリリースします。 154 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 155 * 156 * @param jsonVal 読み込むキー 157 */ 158 private void printSQL() { 159 final Map<String,String> jsonMap = JSONScan.json2Map( jsonVal ); 160 161 if( isDebug() ) { 162 // SQL文を画面に出す関係で、コマンドプロンウトに出すことにする。 163 jsonMap.forEach( (k,v) -> System.out.println( k + ":" + v ) ); 164 } 165 166 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 167 168 final String cols = jsonMap.getOrDefault( "select_cols" , "*" ); 169 buf.append( "select " ).append( cols ).append( " from " ); 170 final String tbl = jsonMap.get( "report_name" ); 171 if( StringUtil.isNull(tbl) ) { 172 final String errMsg = "IOr 検索用 JSON では、report_name(=テーブル名)は必須です。" ; 173 throw new HybsSystemException( errMsg ); 174 } 175 buf.append( tbl ); 176 177 boolean useWhere = true ; // 最初に where キーを出すため 178 useWhere = whereStr( buf , jsonMap.get( "where_lk" ) , " like " , useWhere ) ; // LIKE 検索 179 useWhere = whereStr( buf , jsonMap.get( "where_gt" ) , " > " , useWhere ) ; // GT(>)検索 180 useWhere = whereStr( buf , jsonMap.get( "where_lt" ) , " < " , useWhere ) ; // LT(<)検索 181 182 final String gby = jsonMap.get( "group_by" ); // GROUP BY 183 if( StringUtil.isNotNull(gby) ) { 184 buf.append( " group by " ).append( gby ); 185 } 186 187 final String oby = jsonMap.get( "order_by" ); // ORDER BY 188 if( StringUtil.isNotNull(oby) ) { 189 buf.append( " order by " ).append( oby ); 190 } 191 192 jspPrint( buf.toString() ); 193 } 194 195 /** 196 * 指定の文字列の前後のシングルクオートを削除します。 197 * 198 * @param buf 書き込む文字列バッファ 199 * @param wkey WHERE条件の指定キー 200 * @param operator WHERE条件式(前後にスペースを付けておいてください) 201 * @param useWhere 文字列連結時に、whereを使用済みかどうか 202 * @return where の書き込みがあれば、false を、なければ、引数のuseWhere を返す。 203 */ 204 private boolean whereStr( final StringBuilder buf , final String wkey , final String operator , final boolean useWhere ) { 205 boolean rtnWhere = useWhere ; 206 if( StringUtil.isNotNull(wkey) ) { 207 final String[] ary = JSONScan.json2Array( wkey ); // カンマで分割,JSONScan.json2Trim 済 208 for( final String str : ary ) { 209 final String[] kv = str.split(":", 2); 210 if( kv.length == 2 ) { 211 if( rtnWhere ) { buf.append( " where " ); rtnWhere=false; } 212 else { buf.append( " and " ); } 213 buf.append( quotTrim(kv[0]) ).append( operator ).append( kv[1] ); 214 } 215 } 216 } 217 return rtnWhere ; 218 } 219 220 /** 221 * 指定の文字列の前後のシングルクオートを削除します。 222 * 223 * @param str 読み込む文字列 224 * @return key 前後のシングルクオートを削除した文字列 225 */ 226 private String quotTrim( final String str ) { 227 if( str.length() >= 2 && str.charAt(0) == '\'' && str.charAt(str.length()-1) == '\'' ) { 228 return str.substring(1,str.length()-1); 229 } 230 else { 231 return str ; 232 } 233 } 234 235 /** 236 * 【TAG】POSTリクエストから読み込む場合のキーを指定します。 237 * 238 * @og.tag 239 * POSTリクエストから読み込む場合のキーを指定 240 * 241 * @param key 読み込むキー 242 */ 243 public void setReqKey( final String key ) { 244 reqKey = StringUtil.nval( getRequestParameter( key ),reqKey ); 245 } 246 247 /** 248 * 【TAG】ファイルからURL接続結果に相当するデータを読み取ります。 249 * 250 * @og.tag 251 * 主にデバッグ用として使われます。 252 * 253 * @param file 検索するファイル 254 */ 255 public void setLoadFile( final String file ) { 256 loadFile = StringUtil.nval( getRequestParameter( file ),loadFile ); 257 if( loadFile != null ) { 258 loadFile = HybsSystem.url2dir( StringUtil.urlAppend( fileURL,loadFile ) ); 259 } 260 } 261 262 /** 263 * このオブジェクトの文字列表現を返します。 264 * 基本的にデバッグ目的に使用します。 265 * 266 * @return このクラスの文字列表現 267 * @og.rtnNotNull 268 */ 269 @Override 270 public String toString() { 271 return ToString.title( this.getClass().getName() ) 272 .println( "VERSION" ,VERSION ) 273 .println( "reqKey" ,reqKey ) 274 .println( "loadFile" ,loadFile ) 275 .fixForm().toString() ; 276 } 277}